These are chat archives for ramda/ramda

23rd
Jun 2015
Hardy Jones
@joneshf
Jun 23 2015 00:00
@kedashoe xs.filter(S.Maybe.isJust).sequence(S.Maybe.of).map(R.head)
or so.
find probably is more efficient, yeah?
Kevin Wallace
@kedashoe
Jun 23 2015 00:01
what is .sequence there?
probably so, but still good to see other solutions :)
Hardy Jones
@joneshf
Jun 23 2015 00:02
oh!
doesn't sanctuary have concat?
Kevin Wallace
@kedashoe
Jun 23 2015 00:02
i think it does
Hardy Jones
@joneshf
Jun 23 2015 00:05
In that case.
R.reduce(R.concat, S.Nothing)
oh, wrong kind of concat.
joneshf @joneshf gives up.
Scott Christopher
@scott-christopher
Jun 23 2015 00:07
It’s probably over the top, but you can create a First wrapper for Maybe that acts as the monoid instance that @joneshf was referring to.
David Chambers
@davidchambers
Jun 23 2015 00:12
By the way, each Sanctuary function in the README also has a pretty fragment identifier in addition to --maybe-a--maybe-a---maybe-a mess GitHub generates. For example: https://github.com/plaid/sanctuary#Maybe.prototype.concat.
(Unfortunately I couldn’t figure out a way to make this the identifier GitHub’s little linky thing uses.)
Scott Christopher
@scott-christopher
Jun 23 2015 00:13
Something like:
function First(maybe) {
    if (!(this instanceof First)) return new First(maybe);
    this.maybe = maybe;
}
First.prototype.concat = function(other) {
    return (this.maybe.isJust()) ? this : other;
}
First.prototype.empty = First.empty = function() {
    return First(Nothing)
}

R.reduce(R.concat, First.empty(), listOfFirstMaybes)
Again, it is a little over the top :D
Hardy Jones
@joneshf
Jun 23 2015 00:14
Yeah.
I wonder if you could dump the Maybe prototype onto First and regain all of the rest of the behavior at no extra charge?
Scott Christopher
@scott-christopher
Jun 23 2015 00:15
That would probably work
Or perhaps
R.reduce(R.compose(R.concat, First), First.empty(), listOfMaybes)
Hardy Jones
@joneshf
Jun 23 2015 00:16
yeah.
hmm, just tried prototype way and it didn't like it
oh, apparently I'm on an old version :)
Kevin Wallace
@kedashoe
Jun 23 2015 00:18
gotta run, will check back later, thanks for all the help!
Scott Christopher
@scott-christopher
Jun 23 2015 00:20
Don’t trust my implementation for First either. I just assumed Sanctuary has an isJust method.
David Chambers
@davidchambers
Jun 23 2015 02:33
@scott-christopher, you can use R.is(S.Just).
Scott Christopher
@scott-christopher
Jun 23 2015 02:33
yeah, I just went back to have a look and noticed toBoolean() as well, which would work in this case
David Chambers
@davidchambers
Jun 23 2015 02:34
.toBoolean() is mainly to support S.and and S.or, but yeah, you can use that too. :)
David Chambers
@davidchambers
Jun 23 2015 02:46

@kedashoe, here’s my solution (which I think is the same as yours):

R.pipe(S.find(R.is(S.Just)), R.chain(R.identity))

I feel there must be an approach which avoids the R.chain(R.identity). I’m keen to discover it!

Also, I’m excited to see you playing with Sanctuary!

Jack Jennings
@jackjennings
Jun 23 2015 02:50
@davidchambers anywhere to look at what Sanctuary looks like in real world contexts, beyond what's in the README?
Looks like a very interesting next step after getting the hang of Ramda…
David Chambers
@davidchambers
Jun 23 2015 02:54
Let me find an example!
David Chambers
@davidchambers
Jun 23 2015 03:06

@jackjennings, here’s some real-world code:

// normalizeJson :: String -> String
const normalizeJson =
R.pipe(R.replace('/*-secure-', ''),
       R.replace('-secure-*/', ''),
       R.replace(/\\(?=')/g, ''),
       R.replace(/\\x([0-9A-F]{2})/g,
                 R.pipe(R.nthArg(1),
                        s => parseInt(s, 16),
                        String.fromCharCode)));

// toCheerio :: Object -> Cheerio
const toCheerio =
R.pipe(S.get('more_info'),
       R.map(normalizeJson),
       R.chain(S.parseJson),
       R.chain(S.get('additionalInfo')),
       S.fromMaybe(''),
       cheerio.load);

This is a nice example because there are many operations which may fail:

  • the argument to toCheerio may lack a more_info field;
  • the more_info field may not be a valid JSON string; and
  • the materialized object may not contain an additionInfo property.

We’re able to handle all three failure cases in one step via S.fromMaybe('').

Jack Jennings
@jackjennings
Jun 23 2015 03:28
Great, thanks! I'm going to have to dig in to really get it, but this is absolutely helpful.
David Chambers
@davidchambers
Jun 23 2015 03:39
No problem. Please ask questions here (or in the Sanctuary room) if you’re not sure how to solve a particular problem. Quite a few of us enjoy solving little puzzles along the lines of how do I transform [input] into [output]?
Tobias Pflug
@gilligan
Jun 23 2015 13:33
hm.. any suggestions on how to handle R.filter(someAsyncNodeJsFunc, someList); ?
Raine Virta
@raine
Jun 23 2015 14:16
I didn't remember indexOfhad started to do value based equality, caused some funny results
Scott Christopher
@scott-christopher
Jun 23 2015 14:32
@gilligan If you can separate the async logic from the filtering logic, you could map over the list to produce a list of promises, then sequence it to turn it into a promise of a list. Once you have that you can map over the promise with your filter function to filter the list, resulting in a promise of a filtered list.
Actually, I don't think that will work, because you'll end up with a list of the eventual results of the async function, rather than the original list elements.
Scott Christopher
@scott-christopher
Jun 23 2015 14:43
You could alternatively build up the filtered list using reduce, but it may be a little difficult to type out on my phone :)
David Chambers
@davidchambers
Jun 23 2015 14:54
@raine, we use value-based equality everywhere now except in R.identical. My prediction is that in six months we'll look back at the old equality semantics as barbaric.
Scott Christopher
@scott-christopher
Jun 23 2015 14:59
var filterP = (asyncFilter, xs) =>
  R.reduce((p, x) => p.chain(xs_ =>
    asyncFilter(x).map(keep => keep ? R.append(x, xs_) : xs_)), Promise.of([]), xs)
Assuming you're using FL flavoured Promises.
And no guarantee whether that actually works :P
Scott Christopher
@scott-christopher
Jun 23 2015 15:07
You will need to wrap the nodejs call up in a function that produces a promise (the asyncFilter param in the example).
Tobias Pflug
@gilligan
Jun 23 2015 16:10
Right, thanks guys.
Kevin Wallace
@kedashoe
Jun 23 2015 16:14
I feel there must be an approach which avoids the R.chain(R.identity). I’m keen to discover it!
using reduce as joneshf suggested led me to this
R.reduce((acc, el) => R.is(S.Just, el) ? R.reduced(el) : acc, S.Nothing(), maybes);
which is pretty nice, it short circuits and you either end up with a single Just or Nothing
Hardy Jones
@joneshf
Jun 23 2015 17:06
can someone help me understand the transducer protocol: https://github.com/cognitect-labs/transducers-js#the-transducer-protocol ?
whatis Map in that example?
Hardy Jones
@joneshf
Jun 23 2015 18:11
Where did I mess up here?
function Identity(x) {
  if (!(this instanceof Identity)) {
    return new Identity(x);
  };

  this.runIdentity = function() {
    return x;
  };
}
Identity.prototype['@@transducer/init'] = function() {
  return this;
};

Identity.prototype['@@transducer/result'] = function(x) {
  return x;
};

Identity.prototype['@@transducer/step'] = function(_, y) {
  return Identity(y);
};
R.into(Identity(0), R.map(R.add(1)), Identity(3)); //=> Identity(3)
R.map(R.add(1), Identity(3)); //=> Identity(4)