These are chat archives for ramda/ramda

7th
Jan 2019
Raja Rao DV
@rajaraodv_twitter
Jan 07 18:10 UTC
Do any of you know about "railway oriented programming"? this is a fantastic approach for FP but the code is in F#. Basically it's about error handling..
let me post some pics.. so you'll see what I mean
what I'm looking for is a way to do that using Ramda
Rocky Madden
@rockymadden
Jan 07 19:26 UTC
Fantasy Land, Sanctuary, Fluture, combinators, etc.
Raja Rao DV
@rajaraodv_twitter
Jan 07 19:53 UTC
The above video shows all the details
Screen Shot 2019-01-07 at 11.54.05 AM.png
The above picture shows something called "bind" that somehow converts a method that just takes 1 parameter and returns a result to take 2 parameters, one of them being an optional error or something. And since it can take an optional error parameter, we can combine several such functions
For example: before using "bind"
Screen Shot 2019-01-07 at 11.56.40 AM.png
after using "bind"...
Screen Shot 2019-01-07 at 11.56.57 AM.png
Brad Compton (he/him)
@Bradcomp
Jan 07 19:58 UTC
I'd really suggest taking a look at the Mostly Adequate Guide: https://github.com/MostlyAdequate ad then take a look at something like Sanctuary, Crocks, or Folktale for implementation of the data structures you'll need for that sort of thing
Raja Rao DV
@rajaraodv_twitter
Jan 07 19:58 UTC
.. the three functions "nameNotBlank", "name50", and "emailNotBlank", all throw errors if something isn't blank
Brad Compton (he/him)
@Bradcomp
Jan 07 20:00 UTC
A Monad is what you need to get the bind functionality (called chain in most JS libraries)
Raja Rao DV
@rajaraodv_twitter
Jan 07 20:00 UTC
...but with "bind", we can compose them and if they throw errors, they will still work and will be passed on as-is (like a 2nd railway track)
@Bradcomp , I looked at chain but it doesnt seem to do that exacrtly
Rocky Madden
@rockymadden
Jan 07 20:02 UTC
I'd echo @Bradcomp as well. Those guides are solid.
Raja Rao DV
@rajaraodv_twitter
Jan 07 20:02 UTC
OK, I'll take a look.
Brad Compton (he/him)
@Bradcomp
Jan 07 20:02 UTC
But in your picture, if you take a look at the function signature, it's the same as chain but specific to TwoTrack
Raja Rao DV
@rajaraodv_twitter
Jan 07 20:02 UTC
BTW, if there is a concrete implementation of the "railway oriented programming" in JS, I'd love to see that
I just couldnt find a simple real-world example in JS
Rocky Madden
@rockymadden
Jan 07 20:06 UTC
There is a concrete implementation. In fact, I do "railway oriented programming" all day every day. :point_up: :smile:
Raja Rao DV
@rajaraodv_twitter
Jan 07 20:06 UTC
Cool!
What libraries do you use?
Rocky Madden
@rockymadden
Jan 07 20:07 UTC
Those mentioned above.
Raja Rao DV
@rajaraodv_twitter
Jan 07 20:07 UTC
I dont see fmap, chain, etc in the main Ramda
Brad Compton (he/him)
@Bradcomp
Jan 07 20:07 UTC
fmap is just map, chain is chain
Raja Rao DV
@rajaraodv_twitter
Jan 07 20:09 UTC
I wrote that above blog .. lol
Rocky Madden
@rockymadden
Jan 07 20:09 UTC
That's... hilarious. Hahaha.
Raja Rao DV
@rajaraodv_twitter
Jan 07 20:10 UTC
:)
what I'm trying to figure out is how to implement "ROP" specifically and what's the best libraries to use
Rocky Madden
@rockymadden
Jan 07 20:11 UTC
With that in mind: Either I use Sanctuary. Maybe I use Sanctuary. Pair I use Sanctuary. Future I use Fluture. Others I use Crocks.
Raja Rao DV
@rajaraodv_twitter
Jan 07 20:11 UTC
I see
Rocky Madden
@rockymadden
Jan 07 20:11 UTC
I stick solely to things that adhere to Fantasy Land, as anything which does will work together.
I often favor Static Land, when both are supported (fairly often).
Raja Rao DV
@rajaraodv_twitter
Jan 07 20:12 UTC
Do you use Ramda at all? Or do you use Ramda + Santuary for things like Maybe, Future etc?
I think latter, correct?
Rocky Madden
@rockymadden
Jan 07 20:13 UTC
I've moved over to Sanctuary, due solely to it's opinions aligning with my own. I still do use Ramda where there isn't overlap at times, but it's becoming less common as I grok category theory better and use Sanctuary to build up the equivilent.
Raja Rao DV
@rajaraodv_twitter
Jan 07 20:14 UTC
Does Santuary has everything that Ramda has?
I thought it's a subset of Ramda
Rocky Madden
@rockymadden
Jan 07 20:14 UTC
It does not. Nor does Ramda have everything it has.
Raja Rao DV
@rajaraodv_twitter
Jan 07 20:14 UTC
  • Maybe
I see
OK, could you give me a simple example of two functions, both throw errors, how exactly do you compose them and handle errors using Santuary?
I mean ROP
Rocky Madden
@rockymadden
Jan 07 20:16 UTC
I never have functions that throw.
Return Maybe/Either (encasing those that perhaps throw to get there), sure.
And then, for instance, lift2 or ap to handle those error cases. Yeah, totally.
Raja Rao DV
@rajaraodv_twitter
Jan 07 20:17 UTC
not necessarily throw but, let's say return an error message each
Screen Shot 2019-01-07 at 11.56.21 AM.png
For example the above picture shows 3 functions, that either return a Success, or a Failure with some message
Rocky Madden
@rockymadden
Jan 07 20:18 UTC
Sure, sure. I'd probably use something like tagBy: https://sanctuary.js.org/#tagBy
Or otherwise return an Either (from each), by whatever means seems best fit.
You can then lift3 whatever uses those as params. Any one of them being Left will go on the "failure track".
Rocky Madden
@rockymadden
Jan 07 20:29 UTC
Clear as mud? Haha. I can type it out, if that would be nice :D
Raja Rao DV
@rajaraodv_twitter
Jan 07 21:14 UTC
Thanks!
I'll look at it in more depth..
btw, what's lift1, lift2, lift3 all about?
why are they named like that?
liftA2 is a strange name. It sounds like one of the finicky freight elevators in a rundown factory or a vanity plate for a cheap limo company. Once enlightened, however, it's self explanatory: lift these pieces into the applicative functor world.
Raja Rao DV
@rajaraodv_twitter
Jan 07 21:23 UTC
ah
OK, thanks! I'll take a look!
Rocky Madden
@rockymadden
Jan 07 21:23 UTC
:smile:
Johnny Hauser
@m59peacemaker
Jan 07 21:29 UTC
I have a bunch of operations to format an object. For example:
R.pipe(
  R.path([ 'foo', 'bar' ]),
  R.assoc('baz')
)
ah nm, I need to think on this more
I know this is a common thing
There are all these unique operations on one object in order to form the other one
a bunch of picking, renaming, transforming
I'm not sure how to express it well
ah yeah, I mean objOf('baz') there
I was thinking of doing that and then merging the objects together
Is that reasonable?
Brad Compton (he/him)
@Bradcomp
Jan 07 21:32 UTC
You could use something like converge(unapply(mergeAll), [all, the, transforms])
Ben Briggs
@ben-eb
Jan 07 21:33 UTC
@m59peacemaker I quite like lenses for this https://github.com/calmm-js/partial.lenses#l-pick
Brad Compton (he/him)
@Bradcomp
Jan 07 21:33 UTC
Or else something like applySpec
Ben Briggs
@ben-eb
Jan 07 21:33 UTC
L.get(L.pick({ baz: ['foo', 'bar'] }))
The nice thing about lenses is that they are bidirectional, so you can invert the lens to get your original object shape back out
With the caveat that this does not apply to all partial.lenses :)
Johnny Hauser
@m59peacemaker
Jan 07 21:40 UTC
neat
thanks!!
Ben Briggs
@ben-eb
Jan 07 21:52 UTC
You're welcome :)
Johnny Hauser
@m59peacemaker
Jan 07 21:55 UTC
Is there a more value based cond like
if value is x -> y
if value is a -> b
else -> c
cond([
  [ equals(x), always(y) ],
  [ equals(a), always(b) ],
  [ T, always(c) ]
])
vs
foo([
  [ x, y ],
  [ a, b ],
  [ c ]
])
Ben Briggs
@ben-eb
Jan 07 22:00 UTC
@m59peacemaker Not that I'm aware of no
In this case you can write your own specialised cond - I've used this pattern before as an example;
const ifElseValues = (pred, a, b, z) => pred(z) ? a : b;

ifElseValues(equals(2), "It's two", "It's not two", 2)
Raja Rao DV
@rajaraodv_twitter
Jan 07 22:42 UTC
I have the following code, where I want to count number of courses completed by a student. The courses are course0 and course1. Each object is a student and if the courseCompletionRate for a given course is 1, then that course is completed. If it's less than 1, then the course is incomplete. If the course is missing in the courseCompletionRate, then that course is not taken by the student.
So, I have created the following Ramda code to find the number of students that have completed course0, course1 and number of incomplete courses by the students.
Wondering if we can do it any better than what I've created
const json = [{
    "id": "00BLn3Mz8zeRmeZevhKPrxmM4Ph1",
    "courseCompletionRate": {
        "course0": 1
    },
    "completedChapters": ["course0_chapter1", "course0_chapter2", "course0_chapter3", "course0_chapter4", "course0_chapter5"],
    "incompleteChapters": [],
    "totalCredits": 450
}, {
    "id": "0Aw4orezJHXhQ5E1iFbuAfAhgsS2",
    "courseCompletionRate": {
        "course1": 0.8571428571428571
    },
    "completedChapters": ["course1_chapter1", "course1_chapter2", "course1_chapter3", "course1_chapter4", "course1_chapter5", "course1_chapter6"],
    "specialBadges": {},
    "incompleteChapters": [],
    "totalCredits": 450
}, {
    "id": "0EP1zXHN9gOabbEOvmWU6tWsBuA2",
    "completedChapters": ["course0_chapter1", "course0_chapter2", "course0_chapter3", "course0_chapter4", "course0_chapter5", "course1_chapter1", "course1_chapter2", "course1_chapter3", "course1_chapter4", "course1_chapter5", "course1_chapter6", "course1_chapter7"],
    "specialBadges": {
        "newYear2019Badge": 500
    },
    "incompleteChapters": [],
    "totalCredits": 1650,
    "courseCompletionRate": {
        "course0": 1,
        "course1": 1
    }
}]

const doneCourse0 = pipe(path(['courseCompletionRate']), R.propEq('course0', 1));
const doneCourse1 = pipe(path(['courseCompletionRate']), R.propEq('course1', 1));

const incompleteCourse0 = pipe(path(['courseCompletionRate']), (obj) => obj.course0 ? obj.course0 < 1 : false);

const incompleteCourse1 = pipe(path(['courseCompletionRate']), (obj) => obj.course1 ? obj.course1 < 1 : false);


const countTrueValues = pipe(countBy(identity), pathOr(0, ["true"]));

const completedCourse0Count =pipe(map(doneCourse0), countTrueValues);
const completedCourse1Count =pipe(map(doneCourse1), countTrueValues);
const incompleteCourse0Count =pipe(map(incompleteCourse0), countTrueValues);

const incompleteCourse1Count =pipe(map(incompleteCourse1), countTrueValues);


console.log(completedCourse0Count(json)); //2
console.log(completedCourse1Count(json)); // 1

console.log(incompleteCourse0Count(json)); //0
console.log(incompleteCourse1Count(json)); //1
Johnny Hauser
@m59peacemaker
Jan 07 23:08 UTC
@Bradcomp the plot thickened
I went with your converge approach
But now I've found that several of the transformations start with R.prop('parameters')
because they don't care about the whole object, just that part
Brad Compton (he/him)
@Bradcomp
Jan 07 23:10 UTC
But just some of them, not all?
Johnny Hauser
@m59peacemaker
Jan 07 23:10 UTC
maybe 1/4
enough that I really wouldn't want to be repeating it
I need to make the list being passed to converge using something that only accepts the parameters value as input rather than the whole object
so... I'll need two lists and concat them together for converge
I think
Interesting enough, I think converge could do that, too
Brad Compton (he/him)
@Bradcomp
Jan 07 23:11 UTC
const withParams = map(flip(o)(prop('parameters')), transformationsOfParams)
It totally could :smile:
Johnny Hauser
@m59peacemaker
Jan 07 23:12 UTC
wait huh?
Brad Compton (he/him)
@Bradcomp
Jan 07 23:14 UTC
That code should take a list of transformation functions that operate on the parameters, and map over them to first extract the parameters from the object
So if you split up your transformations into ones that need parameters as the argument and ones that don't, you can map over the ones that do and then concat them with the ones that don't :smile:
Johnny Hauser
@m59peacemaker
Jan 07 23:15 UTC
wow
I could only get that into this codebase if I make it read better
Brad Compton (he/him)
@Bradcomp
Jan 07 23:16 UTC
Hahaha, yeah it's kinda golf-y
Johnny Hauser
@m59peacemaker
Jan 07 23:16 UTC
map flip o prop is not very good English xD
I could try to play it off as normal "oh yeah, that's just a simple mapFlipO on the parameters property"
Brad Compton (he/him)
@Bradcomp
Jan 07 23:17 UTC
flip(o)(prop('parameters')) is just compose(__, prop('parameters'))
SO then when you map that over the transformations it will just add the extraction to the beginning of the function
Johnny Hauser
@m59peacemaker
Jan 07 23:18 UTC
I like it, just gotta think of a name for it
Brad Compton (he/him)
@Bradcomp
Jan 07 23:18 UTC
Haha, yeah
Johnny Hauser
@m59peacemaker
Jan 07 23:19 UTC
ah wait
I think I get it using pipe instead
map (pipe(prop('parameters')) (listOfFns)
same thing?
uh
brain exploded again :/
Brad Compton (he/him)
@Bradcomp
Jan 07 23:21 UTC
I don't think that'll work because pipe is variadic, so it will just take that as a single function pipe and not as a partially applied one
It would work in a perfect world, but this is Javascript lol
Johnny Hauser
@m59peacemaker
Jan 07 23:25 UTC
map
  (fn => pipe(prop('parameters'), fn))
  (listOfFns)
Raja Rao DV
@rajaraodv_twitter
Jan 07 23:25 UTC
Here is the Ramda repl for the thing I was talking earlier. https://goo.gl/SB7TM1
Brad Compton (he/him)
@Bradcomp
Jan 07 23:25 UTC
:point_up: Yep!
Johnny Hauser
@m59peacemaker
Jan 07 23:29 UTC
Wouldn't that just be wrap(prop('parameters')) ?
or some better word for it?
map(wrap(prop('parameters'))) in my case
Brad Compton (he/him)
@Bradcomp
Jan 07 23:32 UTC
I've never used wrap
Johnny Hauser
@m59peacemaker
Jan 07 23:32 UTC
just made it up to name that
a => b => pipe(a, b)
Brad Compton (he/him)
@Bradcomp
Jan 07 23:33 UTC
Sounds like a good name
Johnny Hauser
@m59peacemaker
Jan 07 23:33 UTC
map wrap prop xD
It's not so bad on the brain AND rhymes
Brad Compton (he/him)
@Bradcomp
Jan 07 23:34 UTC
I mean, you should always optimize for rhyming
Johnny Hauser
@m59peacemaker
Jan 07 23:34 UTC
ofc
Brad Compton (he/him)
@Bradcomp
Jan 07 23:37 UTC
There used to be a Ramda function called wrap, but it's gone now...
Johnny Hauser
@m59peacemaker
Jan 07 23:42 UTC
I have a suspicion there's a way to do it just with using a function as a functor
apply(a, b)
or something
Ben Briggs
@ben-eb
Jan 07 23:45 UTC
pipe2 might be a better name for it :)
Alternately
const pipe2 = a => b => x => b(a(x))
Though it's not variadic
Brad Compton (he/him)
@Bradcomp
Jan 07 23:47 UTC
That's a benefit here in this case though
Ben Briggs
@ben-eb
Jan 07 23:50 UTC
Better still! :wink: