These are chat archives for ramda/ramda

29th
Jul 2016
Churchill Lee
@FeliciousX
Jul 29 2016 05:54
hi there ramda community
// reduce :: (b -> a -> b) -> b -> [a] -> b
trying to learn the function signature standards
since reduce can return a type other than a and b .. is it more correct to declare it like this?
//  reduce :: (b -> a -> c) -> b -> [a] -> c
Brad Compton (he/him)
@Bradcomp
Jul 29 2016 06:05

@FeliciousX

The reducing function takes two parameters -> an accumulator and the item in the array. It should return an item of the same type as the accumulator, so that it can reasonably be passed in to the next step in the reduction.

Churchill Lee
@FeliciousX
Jul 29 2016 06:08

ahhh i see @Bradcomp

It should return an item of the same type as the accumulator, so that it can reasonably be passed in to the next step in the reduction

it makes sense now. Thanks !

Brad Compton (he/him)
@Bradcomp
Jul 29 2016 06:10
Awesome!
Churchill Lee
@FeliciousX
Jul 29 2016 06:11
when it clicks.... :) that feeling
Aaron Mc Adam
@aaronmcadam
Jul 29 2016 10:17

I’m trying to refactor this:

const getComparisonTasks = (projectId, taskIds) => {
  const getImages = R.map(fetchData(projectId), taskIds);

  return Promise.all(getImages).then(R.map(getComparisonTask));
};

Here’s what I’m trying, which doesn’t work. I think Promise.all isn’t letting me use it point-free:

const getComparisonTasks = R.compose(
  R.composeP(
    R.map(getComparisonTask),
    Promise.all
  ),
  getImages
);

Here’s the error:

/project/node_modules/ramda/dist/ramda.js:7402
        return _arity(arguments[0].length, reduce(_pipe, arguments[0], tail(arguments)));
                                  ^
TypeError: Cannot read property 'length' of undefined
Rafe
@rjmk
Jul 29 2016 11:31
@aaronmcadam Judging my your error, getImages is not defined at the point you define getComparisonTasks
Aaron Mc Adam
@aaronmcadam
Jul 29 2016 12:25
Yeah, I moved it up, it doesn’t throw an error now, but the promises never resolve, I think
Thanks @rjmk
I’ve got to compare 2 arrays and build a single object with data from both matching iterations. I need a version of intersection that returns the matching pairs instead of creating a set. Any ideas?
const taskLeft = {
  images: [
    {
      id: 'selected.png',
      attributeTitle: 'Selected',
      selected: { label: 'selected.png', path: 'path/to/selected.png' },
      resonance: 4.324,
    }
  ]
};

const taskRight = {
  images: [
    {
      id: 'selected.png',
      attributeTitle: 'Selected',
      selected: { label: 'selected.png', path: 'path/to/selected.png' },
      resonance: 2.016,
    }
  ]
};

const result = [
  {
    id: 'selected.png',
    image: { label: 'selected.png', path: 'path/to/selected.png' },
    attributeTitle: 'Selected',
    resonanceLeft: 4.324,
    resonanceRight: 2.016,
    resonanceDifference: 2.308
  }
];
Sorry for the wall of code :worried:
Martin Broder
@mrtnbroder
Jul 29 2016 12:34
bro
do you even lift?
quick question, I can use lift with Fluture, right?
as it implements the Apply spec (I guess)
David Chambers
@davidchambers
Jul 29 2016 12:38
That should be fine, @mrtnbroder.
Martin Broder
@mrtnbroder
Jul 29 2016 12:56
ty!
Aaron Mc Adam
@aaronmcadam
Jul 29 2016 13:06
So here’s my imperative solution of the problem: http://goo.gl/Kbw1Vy
James Grayling
@jamesgrayling
Jul 29 2016 13:33
This message was deleted
This message was deleted
Denis Stoyanov
@xgrommx
Jul 29 2016 14:09
@aaronmcadam so, you wanna to aggregate intersection values?
Aaron Mc Adam
@aaronmcadam
Jul 29 2016 14:10
yeah @xgrommx, let me update that REPL with what I have so far :)
Here’s my latest attempt: http://goo.gl/cSskJI
The map function has an inconsistent return value though (and object or undefined), which I’d like to fix
I’m not confident with Maybes yet, but I would like to use one here I think
Denis Stoyanov
@xgrommx
Jul 29 2016 14:18
@aaronmcadam compose(filter(notNull), lift((a, b) => a.id === b.id ? someAction() : null))(taskLeft.images, taskRight.images)
notNull = not(isNil)
Denis Stoyanov
@xgrommx
Jul 29 2016 14:23
or if you wanna to use Maybe you can use this callback condition ? Just(someAction()) : Nothing and use smth like catMaybes $ [Just a, Nothing, Just b] => [a, b]
Aaron Mc Adam
@aaronmcadam
Jul 29 2016 14:24
I think I’ll try lift first, thanks a lot!
Aaron Mc Adam
@aaronmcadam
Jul 29 2016 14:26
Is there a better way to do this? { taskLeft: tasks[0], taskRight: tasks[1] }
Denis Stoyanov
@xgrommx
Jul 29 2016 14:27
@aaronmcadam give me 5 min
Aaron Mc Adam
@aaronmcadam
Jul 29 2016 14:27
Alright! :D
Denis Stoyanov
@xgrommx
Jul 29 2016 14:29
@aaronmcadam lift(merge)(compose(objOf('leftTask'), head), compose(objOf('rightTask'), nth(1)))(tasks)
@aaronmcadam or with converge converge(merge, [compose(objOf('leftTask'), head), compose(objOf('rightTask'), nth(1))])(tasks)
Aaron Mc Adam
@aaronmcadam
Jul 29 2016 14:30
Wow, that’s a case of the imperative winning out I think :D
But I do want to learn how to use objOf
Denis Stoyanov
@xgrommx
Jul 29 2016 14:31
@aaronmcadam imperative better?
Aaron Mc Adam
@aaronmcadam
Jul 29 2016 14:31
well, you could say it’s “easier"
Denis Stoyanov
@xgrommx
Jul 29 2016 14:31
@aaronmcadam but now it is pointfree =)
@ram-bot R.objOf
ram-bot
@ram-bot
Jul 29 2016 14:32
[Function: f2]
Brad Compton (he/him)
@Bradcomp
Jul 29 2016 14:32
R.objOf
Denis Stoyanov
@xgrommx
Jul 29 2016 14:32
This message was deleted
ram-bot
@ram-bot
Jul 29 2016 14:32
[Function: f2]
Denis Stoyanov
@xgrommx
Jul 29 2016 14:32
:smile:
Aaron Mc Adam
@aaronmcadam
Jul 29 2016 14:32
ha yea, I like pointfree too, but I think we need to be careful not to overdo things :smile:
      const [taskLeft, taskRight] = tasks;
      return buildComparison({ taskLeft, taskRight })
Brad Compton (he/him)
@Bradcomp
Jul 29 2016 14:32
If you want the signature you don't call Ram Bot
Denis Stoyanov
@xgrommx
Jul 29 2016 14:33
@aaronmcadam yes, this is es6 destructing
Aaron Mc Adam
@aaronmcadam
Jul 29 2016 14:33
yeah, I think that’s the best trade-off for this particular function :)
Denis Stoyanov
@xgrommx
Jul 29 2016 14:34
@aaronmcadam I just provide point free version)) but inside composition you cannot to use destructure assignment =)
Aaron Mc Adam
@aaronmcadam
Jul 29 2016 14:35
ya
It’s really interesting to see the point free versions :)
I appreciate you showing me @xgrommx!
Denis Stoyanov
@xgrommx
Jul 29 2016 14:36
@aaronmcadam welcome =)
Aaron Mc Adam
@aaronmcadam
Jul 29 2016 14:38
We need some Ramda codemods to transform imperative code :D
Aaron Mc Adam
@aaronmcadam
Jul 29 2016 14:39
Yea, I use that page a lot and the “Which function should I use"
Denis Stoyanov
@xgrommx
Jul 29 2016 14:40
@aaronmcadam :+1:
Aaron Mc Adam
@aaronmcadam
Jul 29 2016 14:41
I’m still learning how to compose functions better
Brad Compton (he/him)
@Bradcomp
Jul 29 2016 14:42
I've said this before, but an innerJoin function (like in SQL) would be really nice.
innerJoin:: (a -> b -> Bool) -> (a -> b -> c) -> [a] -> [b] -> [c]
Aaron Mc Adam
@aaronmcadam
Jul 29 2016 14:42
I’m comfortable using list transformations and curry, I’ve been using compose for some stuff. I just need more practice converting funcs to point-free, and start using lift and Maybes
Barry G
@bgits
Jul 29 2016 14:43
This is probably more of FP question than Ramda, but where can I get caught up to speed on Maybe.of and Just() as described in the example for Traverse in the docs?
Brad Compton (he/him)
@Bradcomp
Jul 29 2016 14:43
I wouldn't focus too much on point free. It tends to become obvious over time when there's a good point free version
Aaron Mc Adam
@aaronmcadam
Jul 29 2016 14:44
Well yeah, I start to notice when things don’t seem to allow me to be point-free
I’m trying not to spend ages trying to rethink my functions, and I want to make sure I don’t make the code harder to read just to use compose.
Hi @bgvianyc, this video might help you with Maybe: https://www.youtube.com/watch?v=ZQSU4geXAxM
David Chambers
@davidchambers
Jul 29 2016 14:52
@bgvianyc, you may also find http://sanctuary.js.org/#maybe-type informative.
Denis Stoyanov
@xgrommx
Jul 29 2016 14:55
@aaronmcadam lift is composition of applicative functors liftA2 f a b = fmap f a <*> b but also you can take a look on this version in js https://github.com/xgrommx/from-combinators-to-fp/blob/master/src/index.js#L24
Barry G
@bgits
Jul 29 2016 15:04
@davidchambers Thanks, it's almost clear, except I don't know what a "Just" is
David Chambers
@davidchambers
Jul 29 2016 15:05
Ah, so there are two functions for constructing a value of type Maybe a:
  • Nothing :: -> Maybe a; and
  • Just :: a -> Maybe a.
@ram-bot
S.Nothing()
ram-bot
@ram-bot
Jul 29 2016 15:06
Nothing()
David Chambers
@davidchambers
Jul 29 2016 15:06
@ram-bot
S.Just(42)
ram-bot
@ram-bot
Jul 29 2016 15:06
Just(42)
David Chambers
@davidchambers
Jul 29 2016 15:08
"Nothing" is used to represent failure; "Just" is used to represent success. For example:
@ram-bot
S.parseFloat('XXX')
Barry G
@bgits
Jul 29 2016 15:08
So if you use a Maybe, it will never return "42" as a plain string, if the value if available it will be wrapped in a container, ie: Just()?
ram-bot
@ram-bot
Jul 29 2016 15:08
Nothing()
David Chambers
@davidchambers
Jul 29 2016 15:08
@ram-bot
S.parseFloat('123')
ram-bot
@ram-bot
Jul 29 2016 15:08
Just(123)
David Chambers
@davidchambers
Jul 29 2016 15:08
That's exactly right.
Barry G
@bgits
Jul 29 2016 15:09
@davidchambers I guess this is a deeper questions of why you would want Just(123) returned instead of 123 ?
David Chambers
@davidchambers
Jul 29 2016 15:09
Any time you have a value of type Maybe String you either have Nothing() or Just(s) where s :: String.
Barry G
@bgits
Jul 29 2016 15:10
If I'm interested in the value, it's going to create an additional step of getting the data from the container, why do that?
David Chambers
@davidchambers
Jul 29 2016 15:10
It avoids having to check to see whether the return value is null.
Barry G
@bgits
Jul 29 2016 15:10
ok
David Chambers
@davidchambers
Jul 29 2016 15:10
Imagine that we want to parse a string which we hope represents a number, then negate it. Something like this:
@ram-bot
-parseFloat('123')
ram-bot
@ram-bot
Jul 29 2016 15:11
-123
David Chambers
@davidchambers
Jul 29 2016 15:11
So far so good.
@ram-bot
-parseFloat('XXX')
ram-bot
@ram-bot
Jul 29 2016 15:11
NaN
Barry G
@bgits
Jul 29 2016 15:12
S.parseFloat('XXX') <- would return Nothing() ?
David Chambers
@davidchambers
Jul 29 2016 15:13
That's right.
What this means is that we can use R.map to operate on the return value regardless of whether the parsing operation succeeds or fails.
@ram-bot
R.map(R.negate, S.parseFloat('123'))
ram-bot
@ram-bot
Jul 29 2016 15:14
Just(-123)
David Chambers
@davidchambers
Jul 29 2016 15:14
@ram-bot
R.map(R.negate, S.parseFloat('XXX'))
ram-bot
@ram-bot
Jul 29 2016 15:14
Nothing()
Barry G
@bgits
Jul 29 2016 15:14
i see
thanks
David Chambers
@davidchambers
Jul 29 2016 15:15
We can do other really useful things too. For example, let's say we want to add two numbers only if we're able to successfully parse two strings. Naively we'd do this:
@ram-bot
Number('40') + Number('2')
ram-bot
@ram-bot
Jul 29 2016 15:16
42
David Chambers
@davidchambers
Jul 29 2016 15:16
But what about this?
@ram-bot
Number('40') + Number('XXX')
ram-bot
@ram-bot
Jul 29 2016 15:16
NaN
David Chambers
@davidchambers
Jul 29 2016 15:17
We can use S.lift2 to apply R.add if both parses succeed:
@ram-bot
S.lift2(R.add, S.parseFloat('40'), S.parseFloat('2'))
ram-bot
@ram-bot
Jul 29 2016 15:17
Just(42)
Aaron Mc Adam
@aaronmcadam
Jul 29 2016 15:17
Why lift2? Does it nest it inside 2 containers?
Brad Compton (he/him)
@Bradcomp
Jul 29 2016 15:18
lift2 lifts a binary function
Aaron Mc Adam
@aaronmcadam
Jul 29 2016 15:18
I see
thanks!
David Chambers
@davidchambers
Jul 29 2016 15:19

It takes a regular binary function such as R.add and "lifts" it into the realm of applicative functions. So we start with:

R.add :: Number -> Number -> Number

We transform it to:

S.lift2(R.add) :: Maybe Number -> Maybe Number -> Maybe Number
@ram-bot
S.lift2(R.add, S.parseFloat('40'), S.parseFloat('XXX'))
ram-bot
@ram-bot
Jul 29 2016 15:19
Nothing()
David Chambers
@davidchambers
Jul 29 2016 15:19
R.add will only be applied if we have two "justs".
Barry G
@bgits
Jul 29 2016 15:20
@davidchambers if you don't have two "justs" then a nothing is returned?
David Chambers
@davidchambers
Jul 29 2016 15:20
That's right. :)
Barry G
@bgits
Jul 29 2016 15:22
it's nifty
Aldwin Vlasblom
@Avaq
Jul 29 2016 15:26

@bgvianyc It's about type safety. For example, by default parseFloat returns different types (that is, values that behave in different ways). So you have to inspect the return value before you know what you can do with it:

const result = parseFloat('xxx');
if(!isNaN(result)){
  doTheThing(result);
}

This is something tedious and you might easily forget to do it, leading to all kinds of awful TypeErrors. I wish I could just write one follow-up operation, which works on anything that the function throws at me. In order to be able to do so, the function has to return a uniform type.

Let's consider your earlier question:

an additional step of getting the data from the container, why do that?

Because if you don't, you will end up writing if-statements to determine the type (Nothing and Number are different types). If you do always return the "container", which is always of the same type (Maybe), you can start interfacing with it in a uniform way:

const result = S.parseFloat('xxx');
R.map(doTheThing, result);

The map operation will always succeed, because result is always of the type Maybe, which supports mapping.

David Chambers
@davidchambers
Jul 29 2016 15:28
@bgvianyc, you're right that there is a "cost" to getting the value out (of the maybe), but in my experience this is not something one does often. If we believe that avoiding null, NaN, and other bogus values is a good idea, we'll want to avoid turning a value of type Maybe a into a "nullable" or "NaN-able" if at all possible. One time when it is necessary to perform these conversions is when converting a data structure to JSON.
Aldwin Vlasblom
@Avaq
Jul 29 2016 15:32
Generally you work with these containers throughout your code-base, and only unpack them at the edges of you code: The parts of your program that are capable of deciding how to unpack them. Any parts of your code incapable of making these decisions will simply map over any Functor they get, not caring about the effects it hides, or how they will be unpacked, making most of your code simple and easily testable.
Aaron Mc Adam
@aaronmcadam
Jul 29 2016 15:37
@xgrommx This is what I’ve got so far, if anybody else wants to give their 2 cents: http://goo.gl/Xft4PN
Rafe
@rjmk
Jul 29 2016 15:42
@aaronmcadam @xgrommx A neater pointfree solution is probably to making that object is probably zipObj(['taskLeft', 'taskRight'])
Aaron Mc Adam
@aaronmcadam
Jul 29 2016 15:42
oooo :)
Rafe
@rjmk
Jul 29 2016 15:45
buildComparison could be made pointfree fairly easily (@bradcomp's point about avoiding pointless pointfree is well-taken, but I think this is fairly readable):
const buildComparison = R.compose(
    compact,
    R.lift(matchImage),
    R.pick('images'),
    R.props('taskLeft', 'taskRight')
  )
Aaron Mc Adam
@aaronmcadam
Jul 29 2016 15:47
Why is that Ramda can’t see functions that would be hoisted?
Rafe
@rjmk
Jul 29 2016 15:47
Also, interesting to see matchImage returning null and the compact function in the context of @bgvianyc's, @davidchambers' and @Avaq's discussion of Maybes
Aaron Mc Adam
@aaronmcadam
Jul 29 2016 15:47
I don’t like having to put “private” functions above the public interface, but compose can’t see that they’re defined if I put them after
Rafe
@rjmk
Jul 29 2016 15:48
@aaronmcadam It can. What happens relatively frequently is that pointful functions can reference functions in their body that aren't defined yet, as long as they are when the function is called
Aaron Mc Adam
@aaronmcadam
Jul 29 2016 15:50
that point-free version of buildComparison doesn’t work for me
   1. server › util › buildComparison › builds a Comparison, ordered by Resonance difference DESC
   failed with "f.apply is not a function”
Chet Harrison
@ChetHarrison
Jul 29 2016 15:52
ha @aaronmcadam first time I have had a conversation with the same person on gitter and slack at the same time
Aaron Mc Adam
@aaronmcadam
Jul 29 2016 15:52
haha :)
So yeah, I know I can take images off the args, @ChetHarrison. I was hoping I could use props, but I think that pick is not returning the tuple I need to pass into the lift
Rafe
@rjmk
Jul 29 2016 15:54
When you make a compose, all the functions need to be defined at that point. I did once write a module to get around the problem you mentioned; ultimately I decided to swallow that bullet
Chet Harrison
@ChetHarrison
Jul 29 2016 15:54
I'll actually use my brain this time. what's the latest
Aaron Mc Adam
@aaronmcadam
Jul 29 2016 15:56
@ChetHarrison http://goo.gl/luttCf
Rafe
@rjmk
Jul 29 2016 15:56
Oh yeah sorry there are a few problems with my knowledge of Ramda's API. props should take an array of props. And pick as well
Aaron Mc Adam
@aaronmcadam
Jul 29 2016 15:57
pick returns an object
with the key and the value
@ram-bot R.pick(‘images’, { images: [] })
Rafe
@rjmk
Jul 29 2016 15:58
Oh sorry -- that should be R.pluck
const buildComparison = R.compose(
  compact,
  R.apply(R.lift(matchImage)),
  R.pluck('images'),
  R.props(['taskLeft', 'taskRight'])
)
I think that should be OK
I'm far too dumb to program without a compiler :)
Aaron Mc Adam
@aaronmcadam
Jul 29 2016 16:01
Hmm that doesn’t work either :)
Rafe
@rjmk
Jul 29 2016 16:01
@aaronmcadam Huh! Works for me ...
Aaron Mc Adam
@aaronmcadam
Jul 29 2016 16:02
I’m an idiot, I was passing args instead of a list to props
Can you explain why we need apply?
Rafe
@rjmk
Jul 29 2016 16:05
For sure! lift is supposed to take two arguments. Instead we're passing in an array of two elements. apply is just like calling .apply in JS -- it says the arguments will be provided in an array rather than as individual arguments
Aaron Mc Adam
@aaronmcadam
Jul 29 2016 16:05
Actually I think I get it :)
Chet Harrison
@ChetHarrison
Jul 29 2016 16:06
@aaronmcadam did you get it working?
Aaron Mc Adam
@aaronmcadam
Jul 29 2016 16:06
Ah, nice!
I did, thanks @ChetHarrison and @rjmk !!
@rjmk Have you any tips for keeping track of what functions are “public” with the composition lexical issue?
I think it might be obvious in my codebase because I document the public functions
Denis Stoyanov
@xgrommx
Jul 29 2016 16:08
@rjmk composition could use several parameters only on the start piping
Chet Harrison
@ChetHarrison
Jul 29 2016 16:09
cool
Aaron Mc Adam
@aaronmcadam
Jul 29 2016 16:09
How do you normally organise your modules @ChetHarrison?
Rafe
@rjmk
Jul 29 2016 16:09
@aaronmcadam It's pretty annoying. My rule of thumb is just to reverse the normal (and put a comment in the README in the file saying to look at the bottom for the important functions)
Aaron Mc Adam
@aaronmcadam
Jul 29 2016 16:09
hmm, cool, I guess it’s closer to the main export :)
Rafe
@rjmk
Jul 29 2016 16:10
@xgrommx Could you expand? I think I've lost the context of that comment :)
Denis Stoyanov
@xgrommx
Jul 29 2016 16:10
@aaronmcadam
const buildComparison = R.compose(
  compact,
  R.apply(R.lift(matchImage)),
  R.pluck('images')
);

buildComparison([taskLeft, taskRight]);
Chet Harrison
@ChetHarrison
Jul 29 2016 16:10
@aaronmcadam not sure I understand the question
Denis Stoyanov
@xgrommx
Jul 29 2016 16:11
@aaronmcadam you don't need getting props =)
Rafe
@rjmk
Jul 29 2016 16:11
@aaronmcadam getComparison could be made pointfree fairly easily. Not that it would necessarily be better. Would be interesting to do though
@xgrommx Good point -- it's easy to look at one fn at a time rather than the file as a whole
Aaron Mc Adam
@aaronmcadam
Jul 29 2016 16:11
if you look at the solution (http://goo.gl/0uMXiJ), my “private” implementation functions are now above my main public interface (because R.compose can’t find the references). I was wondering how you handle that normally @ChetHarrison
Denis Stoyanov
@xgrommx
Jul 29 2016 16:12
@rjmk okay compose(add(10), add(20) , multiply)(10, 20) you can use several arguments only for first function of composition because arity for other should be eq 1
Aaron Mc Adam
@aaronmcadam
Jul 29 2016 16:13
This is the calling code:
const mapComparisonTasks = (projectId, taskIds) => {
  return Promise.all(getImages(projectId, taskIds))
    .then(R.map(mapComparisonTask))
    .then(tasks => {
      return buildComparison(R.zipObj(['taskLeft', 'taskRight'], tasks));
    })
    .catch(e => console.log(e));
};
I might end up dropping the keys, but I think they help explain what’s going on within buildComparison
Chet Harrison
@ChetHarrison
Jul 29 2016 16:14
@aaronmcadam I typically just export public stuff and call those functions from a control module
Aaron Mc Adam
@aaronmcadam
Jul 29 2016 16:14
So you compose them elsewhere?
Chet Harrison
@ChetHarrison
Jul 29 2016 16:15
Yes that is my general strategy for keeping private stuff private
Aaron Mc Adam
@aaronmcadam
Jul 29 2016 16:15
I do that too, I just don’t like exposing internals when the public interface is more important
I like that idea though, it would act as an “IOC container” of sorts
Chet Harrison
@ChetHarrison
Jul 29 2016 16:17
I have found a lot of modules and good dir structure is the best defense against rewriting the same code in a different part of a large app
I can't tell you how many times I have tried to solve the same problem a month later because I forgot I already solved it
Rafe
@rjmk
Jul 29 2016 16:19
@xgrommx True. What's the context of that comment?
(Sorry, I imagine it is just me being a bit slow here. Perhaps one of my comments relied on the fact?)
Denis Stoyanov
@xgrommx
Jul 29 2016 16:22
@aaronmcadam your tasks has only 2 elements?
Denis Stoyanov
@xgrommx
Jul 29 2016 17:31
@aaronmcadam converge + lift also will be helpful
const buildComparison = compose(
  compact,
  converge(
    lift(matchImage), [
      path(['taskLeft', 'images']), 
      path(['taskRight', 'images'])
    ]
  )
);
Denis Stoyanov
@xgrommx
Jul 29 2016 19:10
@ram-bot
const replicateM = curry((of, n, x) => sequence(of, repeat(x, n)))

replicateM(always)(10)(map(add(10)))([1,2,3,4])
ram-bot
@ram-bot
Jul 29 2016 19:10
[ [ 11, 12, 13, 14 ],
  [ 11, 12, 13, 14 ],
  [ 11, 12, 13, 14 ],
  [ 11, 12, 13, 14 ],
  [ 11, 12, 13, 14 ],
  [ 11, 12, 13, 14 ],
  [ 11, 12, 13, 14 ],
  [ 11, 12, 13, 14 ],
  [ 11, 12, 13, 14 ],
  [ 11, 12, 13, 14 ] ]
Denis Stoyanov
@xgrommx
Jul 29 2016 19:11
replicateM as in haskell
Michał Nykiel
@marszall87
Jul 29 2016 21:21
hi guys, I'm quite new to Ramda and trying out some stuff
//    findById :: String -> Array -> Object
const findById = R.converge(
  R.find,
  [R.pipe(R.nthArg(0), R.propEq("id")), R.nthArg(1)]
);
why findById isn't auto curried?
and how to do so?
Michał Nykiel
@marszall87
Jul 29 2016 21:28
okay, I think that I've figured it out :) looks like this does what I wanted:
const findById = R.useWith(
    R.find, [R.propEq('id')]
  );
Brad Compton (he/him)
@Bradcomp
Jul 29 2016 21:28

@marszall87

I believe it isn't curried because of a bug in nthArg that was recently fixed in master. See: ramda/ramda#1840

You can actually use useWith instead of converge here, and I think the result would be curried.

You beat me to it :-D
Michał Nykiel
@marszall87
Jul 29 2016 21:28
@Bradcomp thanks, just figured it out at the exact same moment :D
but good to know that this was just a bug, I thought that I've misunderstood something there
Brad Compton (he/him)
@Bradcomp
Jul 29 2016 21:33
Though, can you not just do find(propEq('id'))
Michał Nykiel
@marszall87
Jul 29 2016 21:34
doesn't work for me
Brad Compton (he/him)
@Bradcomp
Jul 29 2016 21:35
R.find
Brad Compton (he/him)
@Bradcomp
Jul 29 2016 21:35
Ah, I see
because the predicate takes a single argument, while propEq takes two... makes sense
Michał Nykiel
@marszall87
Jul 29 2016 21:37
another question, docs say that compose and pipe are not automatically curried, should I just do curry(compose(...)) or are there other ways around?
Brad Compton (he/him)
@Bradcomp
Jul 29 2016 21:39
Correct. You can do curryN to be safe, but looking at the source it will automatically use the arity of the first function to be called.
Michał Nykiel
@marszall87
Jul 29 2016 21:39
and what's the reason behind them not being curried?
just curious
Brad Compton (he/him)
@Bradcomp
Jul 29 2016 21:41
I'm not 100% sure. My guess would be so you can return a higher order function
@ram-bot
const findById = compose(find, propEq('id'))

findById(1)( [{id: 2}, {id: 1}])
ram-bot
@ram-bot
Jul 29 2016 21:41
{ id: 1 }
Brad Compton (he/him)
@Bradcomp
Jul 29 2016 21:43
Unfortunately, that doesn't let you pass both arguments at once
Michał Nykiel
@marszall87
Jul 29 2016 21:46
hmm, it doesn't work with curry for some reason, I have to use curryN:
const findById = R.useWith(
    R.find, [R.propEq('id')]
  );

const getValueForId = R.curryN(2, R.pipe(findById, R.prop('value')));
Brad Compton (he/him)
@Bradcomp
Jul 29 2016 21:47
prop('value') has an arity of one
Oh wait...
Michał Nykiel
@marszall87
Jul 29 2016 21:47
shouldn't it use the arity of first function?
Brad Compton (he/him)
@Bradcomp
Jul 29 2016 21:47
I was reading that as compose
sorry
Michał Nykiel
@marszall87
Jul 29 2016 21:51
ok, I think I have to do this instead:
const findById = R.useWith(
    R.find, [R.propEq('id'), R.identity]
  );

  const getValueForId = R.curry(R.pipe(findById, R.prop('value')));
however it's kinda weird that it worked without identity, just had wrong arity
Denis Stoyanov
@xgrommx
Jul 29 2016 21:53
@marszall87 In useWith should use resultSelector with two or more arguments
Michał Nykiel
@marszall87
Jul 29 2016 21:53
If you expect additional arguments that don't need to be transformed, although you can ignore them, it's best to pass an identity function so that the new function reports the correct arity.
that explains a lot :P
Denis Stoyanov
@xgrommx
Jul 29 2016 21:54
@marszall87 useWith(x => y => x * y, [x => x * 10, x => x * 20])(10)(20)
@marszall87 what do u want to do?
Michał Nykiel
@marszall87
Jul 29 2016 21:56
already figured it out, I wanted a curried function that would return 'value' prop for an element with given 'id'
that last snippet I've posted works for me
Michał Nykiel
@marszall87
Jul 29 2016 22:24
philosophical question this time, not really related to ramda, but I'll ask anyway: how are you naming wrapper functions that return functions with the actual logic? I've seen makeDoSmth convention, but not sure if I like it, e.g.:
function makeGetProject(baseUrl) {
  return id => request.get(`${baseUrl}/project/${id}`);
}

const getProject = makeGetProject('http://localhost:8080');
const project = getProject(7);