These are chat archives for ramda/ramda

3rd
Feb 2016
Brian Gates
@brian-gates
Feb 03 2016 17:46
Anyone got a more readable way to write this?
var relationshipDefinitionsFromRequest = req => R.map(
  rel => R.path(['relationships', rel], getDefinition(req)),
  R.keys(relationshipsFromRequest(req))
);

var disassicationsFromRequest = req =>
  relationshipDefinitionsFromRequest(req)
    .map(definition =>
      disassociateAll(req.params.id, {
        inRels  : definition.inRels,
        outRels : definition.outRels
      })
    )
;
Jakub Korzeniowski
@kujon
Feb 03 2016 18:04
Seeing what do getDefinition and relationshipsFromRequest do would be useful. It's slightly weird that both inputs to map need to know about req (it's like R.map(R.add(arr[7]), arr))
Brian Gates
@brian-gates
Feb 03 2016 18:04
var getDefinition = req => {
  return definitions[R.head(req.labels)];
};
^ don't mind that one, it's ugly :)
@kujon
Julien Goux
@jgoux
Feb 03 2016 18:52
Hello
Is there a function to replace an imperative switch/cases statement with ramda ?
Keith Alexander
@kwijibo
Feb 03 2016 18:58
@jgoux cond ifElse and when
offer ways to approximate it
David Chambers
@davidchambers
Feb 03 2016 18:59
Don't forgot when's sibling, unless.
Keith Alexander
@kwijibo
Feb 03 2016 19:00
ah yes
Julien Goux
@jgoux
Feb 03 2016 19:00
@kw
oops
@kwijibo thanks :)
Keith Alexander
@kwijibo
Feb 03 2016 19:38
Do those of you who use lenses heavily find that it solves the problem of "data structure as a cross-cutting concern"?
(ie, do they let you separate out the "data-access" code from the "business logic" code?)
boxofrox
@boxofrox
Feb 03 2016 19:45

@brian-gates not sure if it's more readable, but here are some alternatives (in theory, some testing needed).

You can compose/pipe...

var relationshipDefinitionsFromRequest = req => R.pipe(
  relationshipsFromRequest,
  R.keys,
  R.map(rel => R.path(['relationships', rel], getDefinition(req)))
)(req);

or

const makePath = (rel) => R.path(['relationships', rel]);
const relationshipGetters = R.pipe(relationshipsFromRequest, R.keys, R.map(makePath));

// :: Request -> [RelationshipDefinition]
const relationshipDefinitionsFromRequest = converge(R.map, [relationshipGetters, getDefinition]);

for disassociationsFromRequest

// type RelationshipSpec = { inRels, outRels }

// :: Id -> RelationshipSpec -> DisassociationResult?
disassociateAll = curryN(2, disassociateAll);   // just making sure it's curried for later

// :: Request -> Id
const toId    = (req) => req.params.id;

// :: RelationDefinition -> RelationshipSpec
const toRels  = R.pick(['inRels', 'outRels']);

// :: Request -> [DisassociationResult]
const disassociationsFromRequest =
  R.converge(R.map,
             [ pipe(toId, disassociateAll),           // partial application: disassociateAll(req.params.id)
                                                      //=> RelationshipSpec -> DisassociationResult
               pipe(
                 relationshipDefinitionsFromRequest,  //=> [RelationshipDefinition]
                 map(toRels)                          //=> [RelationshipSpec]
               ),
             ]);

This may not satisfy your concept of readable--it's certainly less concise--but perhaps telling the same story differently will offer some insight.

boxofrox
@boxofrox
Feb 03 2016 20:06
This message was deleted
Drew
@dtipson
Feb 03 2016 22:11
@kwijibo imho the killer lens feature is just that you can be fully declarative about where you intend a particular operation to be targeted. Everything a lens operation does you could always do in a one-off function, but with lenses, all the implementation details other than where and what are abstracted out (and the abstracted parts are guaranteed to work in a predictable way).
GÁBOR Áron Zsolt
@ashnur
Feb 03 2016 22:15
i really love ramda and fp. but i am not convinced on monads and lenses. particularly lenses seems to be a patch over the problems of monads :-S
doesn't really take the pain away. someone more mathematically and logically inclined might find it more easy to write them, but I rather struggle with them
they tend to spread virally over the codebase :)
Drew
@dtipson
Feb 03 2016 22:18
that means you can write a function to increment a value, and then a lens to focus on a particular value you want incremented, instead of a single function that has to know both where to look AND what to do AND also remember to return the entire datastructure
GÁBOR Áron Zsolt
@ashnur
Feb 03 2016 22:19
yup, but there is a different way, i think
Drew
@dtipson
Feb 03 2016 22:22
not sure I see the connection between lenses and monads. You can write and use lenses without ever using a monad
GÁBOR Áron Zsolt
@ashnur
Feb 03 2016 22:23
right now i am working on putting together a frontend for a rather large and heavy webapp (disclaimer: i hate the term webapp). and the ultimate situation in which i am trying to get at will be a global immutable state. when i get a new state i run a bunch of queries (this is the focus part) and then pass the result of these queries to the views, which are just stateless functions.
at any point if I introduce a monad, i will have to deal with them everywhere. right now I only have it for the async queries with the backend. but to keep it outside of the rest, i need a separate module for it basically
well, using the lenses for not monads is even less usefull, there is R.path() :D
Drew
@dtipson
Feb 03 2016 22:24
Lenses fill in a real gap in Haskell for sure, and there's some controversy about how. But in javascript, how are they patching over anything?
GÁBOR Áron Zsolt
@ashnur
Feb 03 2016 22:25
well, that's my point exactly :)
Drew
@dtipson
Feb 03 2016 22:25
getting the value at a composed path is only one of the things a lens can do (view).
GÁBOR Áron Zsolt
@ashnur
Feb 03 2016 22:25
what are you using it for?
(if not for monads)
Drew
@dtipson
Feb 03 2016 22:26
and sort of the point of monads IS that you have to deal with them: they FORCE you to deal with things like async operations, null values, configuration, IO, etc.
Keith Alexander
@kwijibo
Feb 03 2016 22:26
@dtipson re lenses, yes exactly, that's what I meant by separating out data-access, though I wonder if it works out as well as that in practice
Drew
@dtipson
Feb 03 2016 22:26
instead of all the ways of dealing with them being scattered all over the place
@ashnur you can use them any time you're composing together a bunch of operations
GÁBOR Áron Zsolt
@ashnur
Feb 03 2016 22:29
@dtipson i am pretty sure you just presented a false dichotomy. i can't believe that's the only two situations, using monads or "[having all things monads abstract over] being scattered all over the place"
Drew
@dtipson
Feb 03 2016 22:30
Like, lets say you get a complex user object back from an api that contains, many levels deep, an array of comments by that users that each have titles. You want to capitalize all the titles. Capitalization of strings is dead easy. Property access is dead easy. But instead of writing a one-off imperative solution, you can just do userApi.then(over(makeObjectLens('path.path.path,mapped'), capitalizeString).then(useApi)
GÁBOR Áron Zsolt
@ashnur
Feb 03 2016 22:32
i wish that when i get a response from an api that would be the kind of thing i have to do with the response
Drew
@dtipson
Feb 03 2016 22:32
No Monads (unless Promises are monads), just manipulating data in a unary composition chain
GÁBOR Áron Zsolt
@ashnur
Feb 03 2016 22:32
i definitely would try to invent unnecessarily complex ideas to not get bored, for sure
promises are definitely monads btw, but that's not relevant here
i get what you are saying, i just don't feel the connection with the things i am doing, sorry
however we don't have to think alike :)
kwijibo @kwijibo waits for this to side-track into an "are Promises monads" argument
Drew
@dtipson
Feb 03 2016 22:33
that's fair. And to be fair, the functional/declarative style is a style, and different projects and people have different preferences
I am not smart enough to know whether or not Promises are monads, but my example works exactly the same with just composition over synchronous data
GÁBOR Áron Zsolt
@ashnur
Feb 03 2016 22:35
i just saw your enthusiasm and i would never let pass a situation of making sure that everyone knows someone doesn't like what someone else likes :)
Keith Alexander
@kwijibo
Feb 03 2016 22:37
@ashnur the example that makes me think lenses may be a worthwhile abstraction, is, say you commonly have to access a path like .foo[bar][0].value
and this path (often with slight variations) ends up in many places in your code base
GÁBOR Áron Zsolt
@ashnur
Feb 03 2016 22:38
i really hope that doesn't happen to me, but sadly i can see the situation in which does too possible. however i still think that i can use r.path and if there are variations then those will follow some logic i can abstract over it, i don't see where would lenses help me with this
Drew
@dtipson
Feb 03 2016 22:38
the fact that individual lenses compose is cool, but in one-off situations http://ramdajs.com/docs/#lensPath is probably just fine
Keith Alexander
@kwijibo
Feb 03 2016 22:38
At some point, the code's getting pretty gnarly because of the slight variations in the path you have to check for
GÁBOR Áron Zsolt
@ashnur
Feb 03 2016 22:39
that's one possibility
Drew
@dtipson
Feb 03 2016 22:39
it's that they encapsulate the "apply this operation at this place and return me the original data in the same structure" concept I find really useful
GÁBOR Áron Zsolt
@ashnur
Feb 03 2016 22:40
@dtipson that is actually quite nice, indeed
Keith Alexander
@kwijibo
Feb 03 2016 22:40
And you want to refactor, to simplify the path, but it's a big task because it's hard to find all the different places in your code base it's used
Drew
@dtipson
Feb 03 2016 22:40
(aside from the view operation, which doesn't do that)
GÁBOR Áron Zsolt
@ashnur
Feb 03 2016 22:40
however i think when those situation in which that's useful, something went wrong
Keith Alexander
@kwijibo
Feb 03 2016 22:41
But if you'd used lenses, you only have to edit the lens to refactor the data structure
GÁBOR Áron Zsolt
@ashnur
Feb 03 2016 22:41
because changing something that deep without changing something else means one of the two things. you get way more data than what you need, why do you have everything if you only change a part. OR maybe you need all the data, but you got the wrong format. but then there is a need for normalization which usually means a recursive rewrite anyway
at least for me
Drew
@dtipson
Feb 03 2016 22:41
@kwijibo if you saved an array or string path lookup in one place though, you could just _.get(data, PATHSTRING)
Keith Alexander
@kwijibo
Feb 03 2016 22:42
@dtipson and then you want to set the value as well?
Drew
@dtipson
Feb 03 2016 22:42
there are lots of ways to build and manage paths. I find that the neat thing about lenses is that they functionalize getting and/or setting paths
@kwijibo you use the same pathstring
you can do the exact same thing with lenses really
Lonsdorf's library includes a makeLenses which is basically a way to specify all the path components you might need upfront, and Ramda has lensPath, which does the same and composes them (without exposing them). Whether you can specify the path as an array or a string is a minor implementation detail
though, if you start incorporating constructs like "mapped" into the path, then you start to break away from what a simple pathstring/array could plausibly do
Drew
@dtipson
Feb 03 2016 22:48
R.lensProp('x'); is by itself not that different than x=> x.x or having ['x'] (the latter of which you might wish to pass to, say, a baobab cursor lookup), but the lens version captures the concept in a functional way that can compose with other lenses, get and set in the same operation, etc.
GÁBOR Áron Zsolt
@ashnur
Feb 03 2016 22:53
r.prop('x') is quite functional to me
Keith Alexander
@kwijibo
Feb 03 2016 23:08
Yes, I don't see why composability is a touted benefit of lenses - if anything their composability is potentially confusing because they compose in reverse order
Drew
@dtipson
Feb 03 2016 23:22
well, not really reverse, the composition is still right to left, it's just that what's being composed is configuration :)