These are chat archives for ramda/ramda

21st
Jul 2015
Hardy Jones
@joneshf
Jul 21 2015 03:09
Anyone know of a parser combinator library that doesn't throw an error, but returns actual values?
like Either ParseError a
David Chambers
@davidchambers
Jul 21 2015 03:10
No, sorry.
Scott Sauyet
@CrossEye
Jul 21 2015 03:10
No, but that would be a nice find!
Hardy Jones
@joneshf
Jul 21 2015 03:15
Alright, Guess I'm wrapping someone else's library
Does ramda have an EitherT?
David Chambers
@davidchambers
Jul 21 2015 04:25
No. What is that?
Chris Coniglio
@tencircles
Jul 21 2015 07:56

Hey Ramda noob here, Anybody know if there's a function which maps a list of functions over a function's arguments? e.g.

import {curryN} from "ramda";

function amap (f, ...fns) {
    return curryN(fns.length, function res (...args) {
        let arg_map = (f, i) => f.call(this, args[i]);
        return f.apply(this, fns.map(arg_map));
    });
}

Just wondering if I'm re-inventing the wheel.

Ludwig Magnusson
@TheLudd
Jul 21 2015 15:55
Is anyone here building apps using monads like Maybe, Future, Reader etc. Please ping me if you do. I am running into problems with monads within monads and I am not sure if I am approaching this in the wrong way or if there is a solution to this that I am not aware of
David Chambers
@davidchambers
Jul 21 2015 15:56
I’m using Maybe heavily. I’ve been working to convince my team of the benefits of Future over Promise, but it’s a challenge!
Ludwig Magnusson
@TheLudd
Jul 21 2015 15:57
Have you used Maybe and Futuretogether for anything?
David Chambers
@davidchambers
Jul 21 2015 16:02
I’ve tried to do so. It seems I end up needing to represent every operation which may fail as a Future rather than a Maybe, which is inconvenient. It’s quite possible I simply don’t know how to have the two types work together.
On a related note, I just open sourced https://github.com/plaid/async-problem which compares several approaches to solving a fairly simple asynchronous problem. Spoiler: Future does well. ;)
Ludwig Magnusson
@TheLudd
Jul 21 2015 16:04

Here is a somewhat contrived example of my problems but I think the scenario will resemble situations that would occur in real life:

I want to fetch a user from a database using an id. When the user is fetched I want to get the property name and make it upper case.
Let's say I have the function findById for my database. It takes an id and returns a Future of a Maybe of a user. I then need to chain the fetching of the prop since I use a prop function that returns a Maybe (a user may not have a name)
Here is my code

var app = R.compose(
  R.map(R.map(R.toUpper) 
  R.map(R.chain(maybeProp('name'))
  findById
);
app(42).map(console.log)

This will log the users name upper case if a user with id 42 existis. But it looks pretty messy

Well that I already new ;)
David Chambers
@davidchambers
Jul 21 2015 16:06
That code looks good to me. It makes sense that you need nested maps/chains.
Ludwig Magnusson
@TheLudd
Jul 21 2015 16:06
Ok. But lets crank it up a notch..
David Chambers
@davidchambers
Jul 21 2015 16:08
Though why do you have a Future Error (Maybe User) rather than just a Future Error User? Do you wish to differentiate successful database queries which return no results from legitimate errors, such as there being no database connection?
Ludwig Magnusson
@TheLudd
Jul 21 2015 16:09

Now lets say that I get the input (42) from a request object looking like this:

var req = {
  params: {
    userId: 42
  }
}

I now need to access the param with a Maybe, and the code looks like this:

var app = R.compose(
  R.map(R.map(R.map(R.toUpper))
  R.map(R.map(R.chain(maybeProp('name')))
  R.map(findById)
  maybePath([ 'params', 'userId' ])
);
app(req).map(console.log)

Are we in monad hell now? And I am not sure this actually works...

Yes, I imagined that Future Error represents a failed database query.
I.e. connection error or something like that
David Chambers
@davidchambers
Jul 21 2015 16:14
What do you want to do in the case that the request does not specify a userId?
Ludwig Magnusson
@TheLudd
Jul 21 2015 16:15
Nothing... Perhaps not such a great app, I am just wondering how to handle situations like this.
David Chambers
@davidchambers
Jul 21 2015 16:16
I think it depends on the desired granularity of the error message that may result from the computation.
If we assume for a minute that you don’t care what failed, I think the solution is to avoid deeply nested values.
Ludwig Magnusson
@TheLudd
Jul 21 2015 16:16
Yes. With eithers instead of maybes granular errors could be created. But the app would still be constructed in the same way
David Chambers
@davidchambers
Jul 21 2015 16:17
One could write a function which takes a Maybe a and returns a Future Error a.
So you could represent maybePath([ 'params', 'userId' ]) as a Future instead.
Ludwig Magnusson
@TheLudd
Jul 21 2015 16:18
Like this?
var app = R.compose(
  R.map(R.map(R.toUpper)
  R.map(R.chain(maybeProp('name'))
  // convert to failure if Maybe Nothing
  R.chain(findById)
  Future.fromMaybe
  maybePath([ 'params', 'userId' ])
);
app(req).map(console.log)
David Chambers
@davidchambers
Jul 21 2015 16:21
Yes, that looks like the approach I would take.
Ludwig Magnusson
@TheLudd
Jul 21 2015 16:22
But I am not sure if I like the fact that the Future will contain an error just because there was no match. My idea is that a futrure should reject in the cases when a standard node callback contains an error
And still, if I use that approach, I have solved this specific case. I was wondering if "monad hell" is:
  1. something to expect
  2. a sign of bad design
  3. something that is solved easily with some unknown functon
David Chambers
@davidchambers
Jul 21 2015 16:29
I’m in the same position. I’d like to learn the answer!
Ludwig Magnusson
@TheLudd
Jul 21 2015 16:51
Let me know if you find the answer =)
David Chambers
@davidchambers
Jul 21 2015 16:51
Ditto!
Ludwig Magnusson
@TheLudd
Jul 21 2015 16:53

I did experiment with a function mmap (map map):

var mmap = R.curry(function(f, functor) {
  return R.map(R.map(f), functor);
});

But it is just sugar for 2 level mapping. And you'd need one for each combination of chain/map.

Ludwig Magnusson
@TheLudd
Jul 21 2015 17:08
@davidchambers on a slightly different matter. Are you groking transducers?
David Chambers
@davidchambers
Jul 21 2015 17:48
I wouldn’t say so. I understand them at a high level, but when people ask questions about them I usually think I want to know that too rather than ooh, let me explain. ;)
Ludwig Magnusson
@TheLudd
Jul 21 2015 17:49
Have you watched the vids with Rich Hickery (is that his name? =) )
David Chambers
@davidchambers
Jul 21 2015 17:50
Rich Hickey. Yes, I love his talks. The talk on transducers is high level, though. Boxes and conveyer belts and the like, from memory.
Ludwig Magnusson
@TheLudd
Jul 21 2015 17:52
Yes... In the end he shows a table saying that if we have the function transduce and all other functions we want like map, filter and so on, then we can create new types without the need to implement map and filter. We get them "for free" as long as we make the types transducers. I was wondering if this is possible with types like Maybe and so on
David Chambers
@davidchambers
Jul 21 2015 17:55
That’s another interesting question! Again, I don’t know the answer but would like to.
Ludwig Magnusson
@TheLudd
Jul 21 2015 17:56
I am up for an Identity monad hacking session if anyone is interested...
not now though :P
David Chambers
@davidchambers
Jul 21 2015 17:58
I’m keen to work on that with you. Let’s chat privately to arrange a time.
Jethro Larson
@jethrolarson
Jul 21 2015 18:19
I've been caught with maybe inside of a future chain and it was a headache. I just replaced the maybe with an equivalent future and it all chained better. I'm not sure that's semantically right but it composed well enough that I stopped caring. I expect that this is why folktale calls it "Task" instead of future; so that we think about it more generically than just a replacement for promises.
Ludwig Magnusson
@TheLudd
Jul 21 2015 18:21
@jethrolarson However, I don't think that Futures are the problem. The problem is wrapping one type of monad in another. Two levels is ok but deeper than that is messy.
Jethro Larson
@jethrolarson
Jul 21 2015 18:35
Yeah, before I understood them better I fantasized that they were composable so you didn't end up nesting.
I though that was what chain did. Oh well.
Ludwig Magnusson
@TheLudd
Jul 21 2015 18:36
What do you mean by composable then? Using commute?
Jethro Larson
@jethrolarson
Jul 21 2015 18:39
I don't know what I expected. I didn't understand the landscape
Ludwig Magnusson
@TheLudd
Jul 21 2015 18:39
As is often the case :)
For futures chain is one then the next. When you need the result of one in order to get another
Jethro Larson
@jethrolarson
Jul 21 2015 18:40
Right
chain is just flatmap
Ludwig Magnusson
@TheLudd
Jul 21 2015 18:41
commute can be used to take a list of indepenedent Futures and turn it into one Future with a list of results. Like Promise.all
Jethro Larson
@jethrolarson
Jul 21 2015 18:42
Hmm. I can't say I understand why that works
Ludwig Magnusson
@TheLudd
Jul 21 2015 18:42
Me neither but it does ;)
Jethro Larson
@jethrolarson
Jul 21 2015 18:43
yay math
Ludwig Magnusson
@TheLudd
Jul 21 2015 18:45
I think commute is incorrectly documented. It says it needs a Functor but it needs an Apply
or am I wrong?
No I think it needs an Apply
Jethro Larson
@jethrolarson
Jul 21 2015 18:46
Functor is map + identity, right?
Ludwig Magnusson
@TheLudd
Jul 21 2015 18:46
identity?
functor is map for all I know
Jethro Larson
@jethrolarson
Jul 21 2015 18:47
Yeah, it's just map
a monoid must have a unit value, I think
Ludwig Magnusson
@TheLudd
Jul 21 2015 18:48
Yes the empty thing..
Is that what you meant by identity?
Jethro Larson
@jethrolarson
Jul 21 2015 18:51
Ludwig Magnusson
@TheLudd
Jul 21 2015 18:53
Such abstractness... difficult to grasp
I meant that the monoid contains the empty method according to the Fantasy Land Spec
Jethro Larson
@jethrolarson
Jul 21 2015 18:53
like for addition the identity value is 0
Ludwig Magnusson
@TheLudd
Jul 21 2015 18:53
Yes, and multiplication it is 1
Jethro Larson
@jethrolarson
Jul 21 2015 18:53
Yeah.
Lots of different heady material to struggle to grasp
David Chambers
@davidchambers
Jul 21 2015 18:53
And for composition it is R.identity (or should be!).
Ludwig Magnusson
@TheLudd
Jul 21 2015 18:55
But is there such a thing as an identity value for Maybe?
David Chambers
@davidchambers
Jul 21 2015 18:55
No, there isn’t.
Oops, I was thinking of Either.
Nothing() is the empty Maybe value, is it not?
Ludwig Magnusson
@TheLudd
Jul 21 2015 18:56
What about this Folktales task. Same as Future
Is there an analogy to the "multiply by one" and "add with 0" when talking about Maybe?
Jethro Larson
@jethrolarson
Jul 21 2015 18:58
Isn't it still just function identity?
David Chambers
@davidchambers
Jul 21 2015 18:58
Only if the a in Maybe a has that property.
Jethro Larson
@jethrolarson
Jul 21 2015 19:00
Maybe(a).map(R.identity) is still Maybe(a)
David Chambers
@davidchambers
Jul 21 2015 19:00
We can talk about “multiplying by one” in the same way in the context of the Maybe. Just(1) is the identity element of multiplication of Maybe Number values, for example.
Jethro Larson
@jethrolarson
Jul 21 2015 19:00
That's of course true for all functors
Jethro Larson
@jethrolarson
Jul 21 2015 19:16
I guess function identity is the identity value of map over any functor. Key being that the operation is 'map'. So to figure out the identity value of maybe you have to say what operation you're talking about
There's no Maybe(1).add but you could define one and it's identity value would presumably be 0
or Just(0)?
maybe
Jethro Larson
@jethrolarson
Jul 21 2015 19:27
Since maybe is a functor I guess we don't have to care. I did notice that our implementation map for lenses defines an Identity function https://github.com/ramda/ramda/blob/master/src/over.js
Seems more like a constructor though
David Chambers
@davidchambers
Jul 21 2015 20:02
Yes, it’s the Identity type rather than the identity function.
> Identity(42).map(R.negate)
42
Identity’s map ignores the function it’s given. The value contained in the Identity is fixed.
Oops, I’m thinking of Const.
> Identity(42).map(R.negate)
-42
> Const(42).map(R.negate)
42
Jethro Larson
@jethrolarson
Jul 21 2015 20:20
so much overloading of terms
Ludwig Magnusson
@TheLudd
Jul 21 2015 20:23
Never heard of Const. Is it useful?
David Chambers
@davidchambers
Jul 21 2015 20:24
Yes, it turns out to be useful in the implementation of lenses. Identity and Const make it possible to treat a single function (a lens) as both getter and setter.