These are chat archives for ramda/ramda

15th
Feb 2017
Karan Hiremath
@karanhiremath
Feb 15 2017 00:25

@mimol91 @zwc I'm trying to do something similar where I have a n-nested object which I'm doing some content validation on and I'd like to know if any of the values after my content validation are set to false. I have:

const obj = { a: true, b: { b1: true, b2: { b2a: true, b2b: false }, c: false }

And in the ideal scenario I return the paths [ 'b', 'b2', 'b2b' ] and [ 'c' ] as those are the paths of values that have failed content validation... any thoughts on whether reduce, toPairs, or some combination of the two is the right way to do this?

or is the better answer to just make my content validator smarter so it automatically returns the path of the failed validation...
I'm pretty happy with the validator as of right now though and I think it creates an output that should be pretty easy to do something with. I'm also experimenting just with a recursive filter or map to achieve this although not sure if that will pan out...
Bernhard Wang
@zwc
Feb 15 2017 01:39
@karanhiremath I get as far as getting the false values out, but including the path is a bit trickier. Does [ 'b2b', 'c' ] suffice? In that case, nested reduce works nicely.
const obj = { a: true, b: { b1: true, b2: { b2a: true, b2b: false } }, c: false };

const isFalse = (acc, val) => {
    if(R.last(val) === false) acc.push(R.head(val));
    if(typeof R.last(val) === 'object') {
        return R.reduce(isFalse, [], R.toPairs(R.last(val)));
    }
    return acc;
};

const result = R.reduce(isFalse, [], R.toPairs(obj));
James Forbes
@JAForbes
Feb 15 2017 03:27

@galileopy I recently wrote a post showing the benefits of datatypes like maybe / either etc using JS arrays:
https://james-forbes.com/?/posts/versatility-of-array-methods

Most of the stuff I write is targeted at people that aren't sold on FP yet, which you clearly are, but might still be relevant.

Karan Hiremath
@karanhiremath
Feb 15 2017 05:40
thanks @zwc i think I can spend some time figuring out how to get to the path from there some how… thanks a ton!
Michael Reinig
@MReinigJr
Feb 15 2017 06:33
Hello, I am trying to perform several asyn operations using futures. The initial async operation returns an array of orderIds. The remaining async operations are made per orderId in the orderIds. I got this to work exactly how I would like it to work...I think, but I do not quite understand how ramda knows to wait for each async operation to complete. I originally approached this problem by building a second array of futures based on the ids in the first array. This approach did not work out. I know this is a loaded question, but could someone please explain why this works? I have tried to trip it up by adding delays and such in getEmails. I have also manipulated the orderIds such that I ensure a reject is called in getEmails, but the console.logs in the fork catches it all correctly. I am diving into ramda and ramda fantasy code base, but I was hoping someone could help shed some additional light on my confusion. Thank you so much in advance.
const listEmails = R.curry((gmailApi, query, oauth2Client) => {
  return Future((reject, resolve) => {
    gmailApi.users.messages.list({
      "auth": oauth2Client,
      "userId": "me",
      "q": query}, (err, response) => {
        err ? reject("Gmail Error: " + err) : resolve(response.messages);
      }
    )
  });
});

const getEmails = R.curry((gmailApi, orderIds, oauth2Client) => {
  return Future((reject, resolve) =>
    R.chain(orderId =>
      gmailApi.users.messages.get({
        "auth": oauth2Client,
        "userId": "me",
        "id": orderId,
        "format": "full"
      }, (err, response) => {
        err ? reject("Gmail Error: " + err) : resolve(response);
      }), orderIds
    )
  );
});

const SetUpOrderFutures = listEmails(gmail, "label:test", auth)
  .map(R.map(element => element.id))
  .chain(orderIds => getEmails(gmail, orderIds, auth))
  .map(email => email.payload.body.data)
  .map(emailBody => Buffer.from(emailBody, 'base64'))
  .fork(console.log, console.log);
Karan Hiremath
@karanhiremath
Feb 15 2017 06:52
@zwc thanks a ton - just had to make one small fix - in the iterative step I needed to pass acc into the new reduce function instead of []. Thanks a ton for your help!
Keith Alexander
@kwijibo
Feb 15 2017 07:06
@MReinigJr at a glance getEmails looks odd - the chain inside the Future constructor won't work
it also looks like you're hoping to chain a List with a Future - chain only works to flatten results of the same type
Andrew
@foiseworth
Feb 15 2017 08:15

I just implemented a getIn function (returns a value corresponding to the given path and returns null if the value is undefined ):

function _getIn(path, val) {
  const nextVal = prop(head(path), val);

  if (isNil(nextVal)) return null

  if (length(path) === 1) return nextVal;    

  return _getIn(tail(path), nextVal)
}

Interested in whether it could be done more simply. Link to the repl: https://goo.gl/lSLX1d

Andrew
@foiseworth
Feb 15 2017 08:34
I think this broadly does the same:
const getIn = function(path, obj) {
  return view(lensPath(path), obj)
}
I always struggle with eliminating the outer function in these cases because I feel like I should be able to do:
pipe(lensPath, view)
but as I want obj passed to view and not lensPath that wouldn't work
Najam
@najamkhn
Feb 15 2017 08:56
hmm.. so R.merge() doesn't merge nested objects?
Kurt Milam
@kurtmilam
Feb 15 2017 09:44
@foiseworth You mean like this?
https://goo.gl/ok6gLt
const l = [ 'b', 'c' ]
const o = { a:1, b:{ c: 3 } }

pipe( lensPath, view )( l )( o ) //-> 3
Andrew
@foiseworth
Feb 15 2017 09:45
I do indeed!
So is pipe( lensPath, view )( l ) returning a partial application of view?
Kurt Milam
@kurtmilam
Feb 15 2017 09:46
Yep
view, like most other Ramda methods, is curried and can be partially applied.
Andrew
@foiseworth
Feb 15 2017 09:48
mind blown :clap:
Kurt Milam
@kurtmilam
Feb 15 2017 09:49
:thumbsup:
Andrew
@foiseworth
Feb 15 2017 09:49
I guess I didn't realise that something in a pipe could be curried (apart from the first function) because subsequent functions have to be unary
but I guess in this case we're returning the last function.... which is a curried function
Thanks so much for the help @kurtmilam
:)
Kurt Milam
@kurtmilam
Feb 15 2017 09:58
Right, Ramda methods are curried and can be partially applied, so they're practically unary and n-nary at the same time. You can either pass all of the necessary arguments at once, or you can pass them in n at a time.
Mick Dekkers
@mickdekkers
Feb 15 2017 10:12
Does Ramda have an existing function for checking if a value falls within a range? i.e. min < n < max?
Kurt Milam
@kurtmilam
Feb 15 2017 10:17
@SoullessWaffle There's clamp (source), but it goes beyond just checking whether the number falls within the range.
Stefano Vozza
@svozza
Feb 15 2017 10:20
something like const inRange = (min, max) => R.either(R.gt(R.__, min), R.lt(R.__, max))?
Mick Dekkers
@mickdekkers
Feb 15 2017 10:26
Nice, that would do it but with R.both instead of R.either
Mick Dekkers
@mickdekkers
Feb 15 2017 10:33
I have my range values in an object like { start: 2, end: 14 }. I know I can extract the values by doing R.prop('start') and ditto for end, but I'm not sure how to compose it with inRange since that takes two arguments
I'm fairly new to ramda/fp and this is what I've got so far.
It works but it doesn't look quite right...
const inRange = (min, max) => R.both(R.gt(R.__, min), R.lt(R.__, max))
const getRangeValues = (range) => [R.prop('start', range), R.prop('end', range)]
const isInRangeObj = (range) => R.apply(inRange, getRangeValues(range))

isInRangeObj({
  start: 7,
  end: 14
})(12)
Kurt Milam
@kurtmilam
Feb 15 2017 10:36
Take a look at converge.
converge( inRange, [ prop( 'start' ), prop( 'end' ) ] )( obj )( value )
Mick Dekkers
@mickdekkers
Feb 15 2017 10:40
Nice! That's exactly what I'm looking for
Kurt Milam
@kurtmilam
Feb 15 2017 10:40
:thumbsup:
Mick Dekkers
@mickdekkers
Feb 15 2017 10:42
Thanks :smile:
Mick Dekkers
@mickdekkers
Feb 15 2017 11:05

I'm curious, is there a way to make this pointfree?

const inRange = (min, max) => R.both(R.gt(R.__, min), R.lt(R.__, max))

This is as far as I got before I remembered that lt and gt don't take the same argument

const inRangePf = R.converge(
  R.both,
  [
    R.flip(R.lt),
    R.flip(R.gt)
  ]
)
Kurt Milam
@kurtmilam
Feb 15 2017 11:08
const inRangePf = R.converge(
  R.both,
  [
    R.compose( R.flip(R.lt), prop( 'start' ) ),
    R.compose( R.flip(R.gt), prop( 'end' ) )
  ]
)
Vincent Orr
@Cmdv
Feb 15 2017 11:08
is there a way I could pass through an array of objects and rename some keys in each object then return the new array of objects?
Kurt Milam
@kurtmilam
Feb 15 2017 11:08
That should do the trick, but I haven't tested it.
Mick Dekkers
@mickdekkers
Feb 15 2017 11:12
@kurtmilam sorry I meant the version without the object part, I'd like to be able to reuse it where I might not be using an object to store the range
Vincent Orr
@Cmdv
Feb 15 2017 11:13
[{a: 1, b: 2},{a: 3, b: 4},{a: 5, b: 6}] => [{alo: 1, boo: 2},{alo: 3, boo: 4},{alo: 5, boo: 6}]
Mick Dekkers
@mickdekkers
Feb 15 2017 11:13
actually now I wonder whether it's possible in the first place to make functions that take more than one argument pointfree
Vincent Orr
@Cmdv
Feb 15 2017 11:16
@SoullessWaffle cool that is perfect thanks :)
Mick Dekkers
@mickdekkers
Feb 15 2017 11:16
const renameObjArray = R.map(renameKeys({ a: 'alo', b: 'boo' }))
@Cmdv np :smile:
Vincent Orr
@Cmdv
Feb 15 2017 11:19
yeah I just wan't sure if using a reduce was the right way to go but it looks that way
Kurt Milam
@kurtmilam
Feb 15 2017 11:36
@SoullessWaffle It might be considered cheating, but you could make the function expect a pair in which the first value is start and the second is end, then do:
const inRangePf = R.compose( R.converge(
  R.both,
  [
    R.compose( R.flip(R.lt), head ),
    R.compose( R.flip(R.gt), last )
  ]
),
pair
)
const betweenOneAndTen = inRangePf( 1, 10 )
betweenOneAndTen ( 5 ) // -> true
I'd actually like to know whether this sort of construction is considered harmful.
Kurt Milam
@kurtmilam
Feb 15 2017 11:42
The same could be done by having the function expect an object with start and end properties, then build the object from the first and second arguments before passing it into the both.
But again, I don't know whether that's considered copacetic in good fp.
Sorry, the above needs some work - I typed it out without testing it. Will try to figure out where I went wrong.
Kurt Milam
@kurtmilam
Feb 15 2017 11:48
Galileo Sanchez
@galileopy
Feb 15 2017 11:59
@JAForbes Your post is awesome, specifically the part where it sums up the pros of having less syntax variety. And is eye friendly. It would be useful to as a teaching aid with coworkes too. Thank you very much. It would be nice though that it states somewhere that there is no need to alter every Type prototype, and tell there that there is a nice type around for handling just this case and perhaps a reference to Maybe. @kwjibo FP the little parts was enlightening, and your post was a bit advanced perhaps, it does assume familiarity with boths types, although it addresses a very interesting topic which is chaining different types and I liked that, it would be nice though to have a reminder there that Tasks and Maybes and perhaps Either all have some things in common, like most are [Monoid?, Functor, ...], this will be a nice entry point to actually starting to understand why the fantasy-land spec is important.
James Forbes
@JAForbes
Feb 15 2017 12:02
@galileopy yeah great points. I always try to walk the fine line between mentioning / referencing terminology, because I have a strong belief it causes most non FP readers to immediately skim and feel its not for them.
which is sad, but from what I've observed true :(
oh and thank you!
Galileo Sanchez
@galileopy
Feb 15 2017 12:05
@JAForbes IMHO, it could be at the end, something like, "if you liked this pattern you should check out a type made exclusively for this use case" and a link to maybe? so you keep the same approach, and lead those interested to keep learning. :)
Kurt Milam
@kurtmilam
Feb 15 2017 12:14
@JAForbes Good article. Thanks for sharing it. A couple of corrections:
  1. 'disect' -> 'dissect'
  2. get('a', { a: 2}) //=> a -> get('a', { a: 2}) //=> 2 (I think)
James Forbes
@JAForbes
Feb 15 2017 12:15
thanks @kurtmilam :D I'll fix that right now

@galileopy at the very end there's a post script that says exactly that, but it might need to happen in a separate section

This article has been a covert introduction into the Maybe and Either Monad. If you want to try out the real thing check out Sanctuary
I also recommend checking out Railway Oriented Programming by Scott Wlaschin. It's a helpful introduction to the conceptual framework and justifications for avoiding conditions and exceptions.

Galileo Sanchez
@galileopy
Feb 15 2017 12:19
:( my bad, missed it
@JAForbes thanks for the clarification, I added the Railway Oriented Programming to my to-read list.
James Forbes
@JAForbes
Feb 15 2017 12:22
no I think it needs to be at the top of the post
its too easy to miss, and without it, people might get the wrong idea
I'm doing some work on the site now so I'll take a look
Galileo Sanchez
@galileopy
Feb 15 2017 12:22
Or maybe be the opening for the last section?
James Forbes
@JAForbes
Feb 15 2017 12:23
yeah, that's better
less likely to scare away non FP'ers, but also less likely to be missed
Galileo Sanchez
@galileopy
Feb 15 2017 12:23
True that.
Kurt Milam
@kurtmilam
Feb 15 2017 12:24
'Even if your not 100% sold' -> 'Even if you're not 100% sold'
Hope you don't mind the suggested edits.
Very good article. It offered concrete illustrations of some ideas that have been incubating in my head. It is also an excellent covert introduction to Maybe and Either, and it made that ROP article more clearly understandable for me, as well.
Galileo Sanchez
@galileopy
Feb 15 2017 12:30
what are some good fantasy-land compatible Monads implementation, there are so many maybes, should I just pick one without worrying too much?
Galileo Sanchez
@galileopy
Feb 15 2017 12:37
now I understand why R.map dispatches to the map method of the received object, it makes working with other functors, just as conveniently as using them with js arrays :)
Rick Medina
@rickmed
Feb 15 2017 13:15
@galileopy I personally like lightweight/individual packages so I use data.maybe, data.either, etc and they're well documented
Keith Alexander
@kwijibo
Feb 15 2017 13:23
me too
I've also used sanctuary a bit - but been having performance problems (which may be fixed in the last release)
Keith Alexander
@kwijibo
Feb 15 2017 13:28
the docs in ramda-fantasy have good usage examples - and the API is much the same as folktale, so they are worth reading even if you are using a different library
Galileo Sanchez
@galileopy
Feb 15 2017 13:31
I'll give ramda-fantasy a try, I think that having multiple monads available in one package will encourage me to start learning to use the others :)
Galileo Sanchez
@galileopy
Feb 15 2017 13:36
I should be careful though, Maybe.of() returns Just undefined o.O
Aldwin Vlasblom
@Avaq
Feb 15 2017 13:38
It's supposed to be that way.
James Forbes
@JAForbes
Feb 15 2017 13:39
interesting, what were you expecting @galileopy ?
Aldwin Vlasblom
@Avaq
Feb 15 2017 13:40
That's like expecting Promise.resolve(new Error) to give you a rejected Promise.
Galileo Sanchez
@galileopy
Feb 15 2017 13:41
idk, solved it with this const toMaybe = R.cond([[R.isNil, Maybe.Nothing], [R.T, Maybe.of]]) :)
Aldwin Vlasblom
@Avaq
Feb 15 2017 13:41
:+1:
or simpler: toMaybe = ifElse(isNil, Maybe.Nothing, Maybe.of)
Galileo Sanchez
@galileopy
Feb 15 2017 13:43
:)
Keith Alexander
@kwijibo
Feb 15 2017 13:44
sanctuary and folktale call that functionfromNullable

@galileopy

your post was a bit advanced perhaps, it does assume familiarity with boths types

both types?

Rick Medina
@rickmed
Feb 15 2017 13:59
poll: do you ever use this when using ramda?
Bernhard Wang
@zwc
Feb 15 2017 14:00
I stay away from this as much as I can. So goes for ramda. :)
Aldwin Vlasblom
@Avaq
Feb 15 2017 14:00
:x:
Rick Medina
@rickmed
Feb 15 2017 14:01
(read: I never ever ever use this vs I need to use it in the rare occasion for some reason)
Bernhard Wang
@zwc
Feb 15 2017 14:01
Sign me up for never use. :)
Stefano Vozza
@svozza
Feb 15 2017 14:11
never
Galileo Sanchez
@galileopy
Feb 15 2017 14:15
@kwijibo sorry, both.
Keith Alexander
@kwijibo
Feb 15 2017 14:16
@galileopy what are both types? (I wasn't picking you up on a typo!)
Galileo Sanchez
@galileopy
Feb 15 2017 14:17
I found this post to be useful http://jrsinclair.com/articles/2016/marvellously-mysterious-javascript-maybe-monad/, idk if I got that from here
Future is a type and so is Maybe
Keith Alexander
@kwijibo
Feb 15 2017 14:22
I kinda love the styling of the code samples in the jrsinclair blog
@galileopy maybe you read a different post of mine from the one I intended (https://kwijibo.github.io/uncertain-values/) ?
Galileo Sanchez
@galileopy
Feb 15 2017 14:23
@kwijibo yes, I did, gots lots of links scheduled XD. sorry for that.
Keith Alexander
@kwijibo
Feb 15 2017 14:26
@galileopy not at all - just that post does try to explain Maybe from first principles, so I wondered if I'd explained it badly, or if you'd read a different post
Galileo Sanchez
@galileopy
Feb 15 2017 14:27
@kwijibo you nailed it, I mixed it with Another empty future, :) the title got my attention
Drew
@dtipson
Feb 15 2017 14:38
Imo, if you're going to use Either and Maybe, might as well just use Either for everything in most cases. Only place Either starts to diverge from Maybe in utility is when you get into the particular implementations of things like concat/alt, which might differ
but by and large, Either is more flexible but can also cover a lot of the use-cases that Maybe can
Ryan Zeigler
@rzeigler
Feb 15 2017 14:49
almost everytime I start with a maybe I end up with an either
maybe feels useful if only 1 type of error can occur, but generally, you are reporting to something a bit more general, so it ends up with a coercion to encode the implicit knowledge anyway
Rick Medina
@rickmed
Feb 15 2017 14:55
so, eg, safeHead you would return a Left of an empty string?
or give the specific (only) reason on all the would have been maybes? eg, Left('empty array')
Drew
@dtipson
Feb 15 2017 15:03
Right. Or with a safe path lookup using Either, you can be descriptive about where the lookup failed (which level wasn't there)
Rick Medina
@rickmed
Feb 15 2017 15:21
for what do you use those kind of errors for? runtime logging?
Michael Reinig
@MReinigJr
Feb 15 2017 15:47

@kwijibo Thanks for the response. The R.chain in the future is really throwing me off as well, but it does work as shown. However, I need to execute a series of futures based upon the results in a list, but I do not want a single failure to cause the other futures I am executing to fail. In the future docs, there is this example:

//:: String -> Future Error [String]
ls = path => Future((reject, resolve) =>
  fs.readdir(path, (err, files) => err ? reject(err) : resolve(files)));

//:: String -> Future Error String
cat = file => Future((reject, resolve) =>
  fs.readFile(file, 'utf8', (err, data) => err ? reject(err) : resolve(data)));

//:: String -> Future Error String
catDir = dir => ls(dir).chain(R.traverse(Future.of, cat)).map(R.join('\n'));

I replicated this and originally had my getEmails function only take one id and return a future. But using R.traverse caused a single reject to make them all reject. As a side note, if I use R.map in getEmails instead of R.chain, it still works. However, I can not switch .chain in SetUpOrderFutures to a .map. Based upon the type signatures of R.chain and R.map, this kind of makes sense to me. There is a very curious note for R.chain in the docs though:

Dispatches to the chain method of the second argument, if present, according to the FantasyLand Chain spec.

I wonder if this plays into it all. Thanks again for your help and to anyone else that chimes in.

Vincent Orr
@Cmdv
Feb 15 2017 16:29

been trying to do this all afternoon in one pass but I just can't quite get it:

[{"openTime":"2017-02-15 15:45:00"},
    {openTime":"2017-02-18 22:45:00"}] =>
[{key: 0, "title": "Wed, 2nd Feb" "openTime":"2017-02-15 15:45:00", },
 {key: 1, "title": "Wed, 2nd Feb" "openTime":"2017-02-15 15:45:00", }]

so I'm trying to add new key from current index, then I want to use the value of openTimes to create title!!

I have a feeling that it might be easier to do in multiple passes but even adding a new object key via a function taking the values from current object would be great
Brad Compton (he/him)
@Bradcomp
Feb 15 2017 16:34
Does it need to be point free? https://goo.gl/we8gV4
Vincent Orr
@Cmdv
Feb 15 2017 16:35
@Bradcomp whoa I so overcomplicated my efforts hahaha
Brad Compton (he/him)
@Bradcomp
Feb 15 2017 16:35
lol
Vincent Orr
@Cmdv
Feb 15 2017 16:36
it's fine not been pointfree, I hadn't spotted addIndex
well I gave it a good try that's what counts, what if the object had lots of other values would I need to manually add them too?
to be honest the way I feel now think I will :)
Brad Compton (he/him)
@Bradcomp
Feb 15 2017 16:39
I mean, it depends on how complicate it is
Drew
@dtipson
Feb 15 2017 16:39
@rickmed sure: though it's all flexible enough that you can use it for anything you want
Brad Compton (he/him)
@Bradcomp
Feb 15 2017 16:39
If you have a lot of properties, and only a few need changing, some combo of assoc and evolve can help
Vincent Orr
@Cmdv
Feb 15 2017 16:40
@Bradcomp think it's like 8 in total
I'll look into assoc & evolve :)
Brad Compton (he/him)
@Bradcomp
Feb 15 2017 16:41
As far as the one pass thing, remember the law that compose(map(f), map(g)) == map(compose(f, g))
Galileo Sanchez
@galileopy
Feb 15 2017 16:41
@kwijibo excellent post! and it really is an excellent way to continue after @JAForbes post on maybes. I liked it very much, and they way it shows how to use with promises in the examples is a nice touch.
Rick Medina
@rickmed
Feb 15 2017 16:42
@dtipson I haven't needed those kind of errors yet (they are not translatable to the user) but good point about making it more flexible. Could you think of cons using all eithers?
Vincent Orr
@Cmdv
Feb 15 2017 16:42
@Bradcomp been doing a lot of Haskell lately so my brain has gone to mush when doing JS for some reason
think evolve will do the trick here thanks for the help :)
Brad Compton (he/him)
@Bradcomp
Feb 15 2017 16:56
:+1:
Drew
@dtipson
Feb 15 2017 17:52
Not really. I mean, like I noted above, there's sometimes a different intuition about how .concat/.alt etc. would be implemented in Either vs Maybe, so if you start to get into using those, might be a difference. Lonsdorf first suggested going w/ all Eithers at a conference, and I've found it to be pretty universally true. Once I started using and implementing things in Eithers, I never looked back
and haven't hit a case where it's caused a problem
Emilio Srougo
@Emilios1995
Feb 15 2017 18:38
Hi, How would you implement Underscore's matcher
with Ramda?
@ram-bot
R.whereEq({ selected: true, visible: true })({ selected: true, visible: true, foo: 'bar' });
ram-bot
@ram-bot
Feb 15 2017 18:42
true
Emilio Srougo
@Emilios1995
Feb 15 2017 18:44
@alex-deas That's it. Thanks!
Mick Dekkers
@mickdekkers
Feb 15 2017 19:01
@kurtmilam thanks again for your help earlier today. It's pretty cool how you can use pair to make multiple-parameter functions pointfree
Rick Medina
@rickmed
Feb 15 2017 19:12
@dtipson cool thanks, I'll give it a try -it obviously makes monadic composition simpler.
Drew
@dtipson
Feb 15 2017 21:57
yeah, I mean, if you're ever finding yourself in a situation where you have Maybes inside Eithers or vice versa, you're probably doing a lot of extra work for nothing