These are chat archives for ramda/ramda

3rd
Jul 2018
Nicolás Fantone
@nfantone
Jul 03 2018 00:08
hi peeps, I joined because I was having some trouble understanding the behavior of converge in some cases
I have something that is meant to work like the following:
propSatisfies(flip(contains)([1,2,3,4]), 'x')({x: 7})
basically, return true or false if an object has a prop x contained in some array of fixed values
(above would return false because 7 is not in [1,2,3,4])
I need that array of values to also be passed in as an argument, so I was playing around with converge
converge(propSatisfies, [flip(contains), always('id')])([1,2,3,4])
I was expecting that to return a function to which I could pass my { x: 7 } object, but that doesn't seem to be the case... and I can't quite understand why
Nicolás Fantone
@nfantone
Jul 03 2018 00:14
it just keeps returning functions (as if asking for arguments indefinitely)
I found out that if I wrap my flip(contains) in a compose or o, it starts working
converge(propSatisfies, [o(flip(contains), identity), always('x')])([1,2,3])
:point_up: that works fine
const isXOneOf = converge(propSatisfies, [o(flip(contains), identity), always('x')])([1,2,3]);
isXOneOf([1,2,3,4])({ x: 3 }); // true
so what's going on?
Nicolás Fantone
@nfantone
Jul 03 2018 00:21
> flip(contains)([1,2,3])(3)
true
> o(flip(contains), identity)([1,2,3])(3)
true
:point_up: these two look identical to me
Nicolás Fantone
@nfantone
Jul 03 2018 00:27
nAry(1, flip(contains)) or curryN(1, flip(contains)) also seem to work as intended
Nicolás Fantone
@nfantone
Jul 03 2018 00:36
looks to me that it only does it job when a unary function is passed in as a branching function of converge
Nicolás Fantone
@nfantone
Jul 03 2018 00:56
switched to useWith in the end, which seems to work fine for me in this case
useWith(propSatisfies(__, 'x'), [flip(contains), identity])([1,2,3], { x: 6 })
Brad Compton (he/him)
@Bradcomp
Jul 03 2018 02:19
The issue is that converge curries the resultant function to the highest arity of the branching functions. In this case, flip(contains) has an arity of two, so the function won't run until it is provided with two parameters. By using o(x, identity) or nAry or curryN(1) you are forcing the arity of flip(contains) to 1, thus changing the arity of the overall function
It's one of those edge cases where the expected behavior really depends on the use case.
Jacob Bogers
@Jacob_Bogers_twitter
Jul 03 2018 07:32
_keys doesnt exist
also keys doesnt exist
R.keys({a: 1, b: 2, c: 3}); //=> ['a', 'b', 'c']
var R = require('rambda')
I imported like this
in the package.json i see "version": "1.1.5"
why is .keys in the documentation but doesnt exist in the source
Jacob Bogers
@Jacob_Bogers_twitter
Jul 03 2018 07:52
LOL,
wrong package
ramBda
instead of ramda
Nicolás Fantone
@nfantone
Jul 03 2018 12:25
@Bradcomp Gotcha. But in this case, the resulting function is not invoked, even if passing in two arguments
> converge(propSatisfies, [flip(contains), always('id')])([1,2,3,4])
[Function]
> converge(propSatisfies, [flip(contains), always('id')])([1,2,3,4])('foo')
[Function: f1]
> converge(propSatisfies, [flip(contains), always('id')])([1,2,3,4])('foo')('bar')
TypeError: pred is not a function
looks like it consumes three arguments, before failing
although:
> converge(propSatisfies, [flip(contains), always('id')]).length
2
Nicolás Fantone
@nfantone
Jul 03 2018 12:31
/shrug
Brad Compton (he/him)
@Bradcomp
Jul 03 2018 13:19
PropSatisfies takes 3 arguments. So once the function returned by converge consumes two you still have one more to provide
Nicolás Fantone
@nfantone
Jul 03 2018 14:17
I'm clearly understanding this wrong, but isn't converge providing arguments to propSatisfies?
converge(propSatisfies, [flip(contains), always('id')])([1,2,3,4]) <- after this call, I'd expect to have something like a partially applied propSatisfies with flip(contains)([1,2,3,4]) as first argument and 'id' as its second
so, in that scenario, there's only one argument remaining
where am I wrong?
(also, many thanks for the answers! @Bradcomp)
Brad Compton (he/him)
@Bradcomp
Jul 03 2018 14:33

It might help to break it down in steps.

converge(propSatisfies, [flip(contains), always('id')])
//is equivalent to
curryN(2, (x, y) => propSatisfies(flip(contains)(x, y), always(id)(x, y)));

flip(contains)([1, 2, 3, 4])('foo') === false
always('id')(/*...*/) === 'id'
//leaves
propSatisfies(false, 'id')('bar');

So the error comes because false is not a valid predicate for propSatisfies, but it doesn't get evaluated until the final parameter to propSatisfies, 'bar' is passed.

The issue comes because converge wants to fully evaluate its branching functions before passing the result to the converging function. That's not what you want in your case. You want flip(contains) to take one argument, then be passed to propSatisfies, so it has to be wrapped to work with converge

converge passes all the arguments to each branching function, while useWith matches up each parameter with the corresponding branching function
so useWith is probably what you are looking for here.
Nicolás Fantone
@nfantone
Jul 03 2018 14:40
:clap:

The issue comes because converge wants to fully evaluate its branching functions before passing the result to the converging function.

When you say "fully evaluate", you mean exhausting its arguments?

so, basically, you cannot have a branching higher order function?
I did end up using useWith :)
Nicolás Fantone
@nfantone
Jul 03 2018 14:47
I'm guessing the HoF should not be curried if we want to use it with converge and not provide all of its arguments?
// This works!
> converge(propSatisfies, [x => y => x + y === 42, always('id')])(2)({ id: 40 })
true

// ...while this doesn't
> converge(propSatisfies, [curry((x, y) => x + y === 42), always('id')])(2)({ id: 40 })
[Function: f1]
Nicolás Fantone
@nfantone
Jul 03 2018 14:53
isn't this kind of limiting/surprising given that all of Ramda's functions are curried?
Brad Compton (he/him)
@Bradcomp
Jul 03 2018 15:16

Ramda uses Function.length a lot to determine how many arguments a function takes. curry maintains the length of a function, as opposed to manually currying like in the first example, which always gives a length of one.

convergeneeds to make a determination on how many arguments the resultant function will take, so it can curry that function correctly.

The alternative, taken by compose, is to not curry the resultant function. I think that's a valid choice here too, but not the one that was taken.

I agree it's one of the gotcha's of the library. Unfortunately it's a case where there are multiple design goals of Ramda that come into conflict.

Nicolás Fantone
@nfantone
Jul 03 2018 15:19
thanks a mill for the explanation :)
I'll be more careful with my converges from now on
Mike Lambert
@lax4mike
Jul 03 2018 17:07
@rambot
R.compose(
  R.tap(x => console.log(x))
)(null)
bleh, well, the result is "Cannot read property '@@transducer/step' of null"
is this a bug?
Rob Hilgefort
@rjhilgefort
Jul 03 2018 17:08
That was changed in 0.25
Mike Lambert
@lax4mike
Jul 03 2018 17:08
what was changed?
Rob Hilgefort
@rjhilgefort
Jul 03 2018 17:08
Seems to be the intended functionality but it broke a bunch of stuff for me as well.
I used to do tap(console.log)
but now tap treats a function in that position as a transducer step
Mike Lambert
@lax4mike
Jul 03 2018 17:09
that doesn't work either
Rob Hilgefort
@rjhilgefort
Jul 03 2018 17:09
yeah, it doesn’t
Mike Lambert
@lax4mike
Jul 03 2018 17:09
@ram-bot
R.compose(
  R.tap(console.log)
)(null)
wtf @ram-bot
Rob Hilgefort
@rjhilgefort
Jul 03 2018 17:10
I resigned myself to making trace
const trace = x => {
  console.log(x);
  return x;
};
Mike Lambert
@lax4mike
Jul 03 2018 17:10
what's the actual change?
that's the only situation that i use tap...
Rob Hilgefort
@rjhilgefort
Jul 03 2018 17:11
ramda/ramda#2319
@lax4mike I use it for some other side effecting functions that I don’t care about the return for
but yeah, that was my most common use case
Mike Lambert
@lax4mike
Jul 03 2018 17:11
Tap will act as tranducer if given a transformer in the second parameter
2nd parameter, this function is the first
ramda/ramda#2229
Rob Hilgefort
@rjhilgefort
Jul 03 2018 17:12
¯_(ツ)_/¯ I basically don’t use tap anymore
Mike Lambert
@lax4mike
Jul 03 2018 17:12
yeah, this pretty much breaks it
it doesn't seem right to me...
Rob Hilgefort
@rjhilgefort
Jul 03 2018 17:12
I just make traceeverywhere I go and use that
Mike Lambert
@lax4mike
Jul 03 2018 17:12
:thumbsdown:
i'll comment on that issue
Rob Hilgefort
@rjhilgefort
Jul 03 2018 17:13
Make an issue to spark a discussion- maybe we’ll get more info than what we got on ramda/ramda#2319

@lax4mike You might like to know about a handy package called treis

https://github.com/raine/treis

I use that more often than anything else these days for debugging a compose
Mike Lambert
@lax4mike
Jul 03 2018 17:14
i mean, ramda should probably just have R.log or something
Rob Hilgefort
@rjhilgefort
Jul 03 2018 17:15
¯_(ツ)_/¯
Brad Compton (he/him)
@Bradcomp
Jul 03 2018 17:41
I believe this is fixed, but stuck because nobody has released a new version
Mike Lambert
@lax4mike
Jul 03 2018 19:55
it appears that way