These are chat archives for ramda/ramda

3rd
Dec 2016
Johnny Hauser
@m59peacemaker
Dec 03 2016 04:31

I have code like this:

R.pipe(
  R.over( // keep only foos that have bar
    R.lensProp('foo'),
    R.filter(R.has('bar'))
  ),
  R.over( // transform qux
    R.lensProp('qux'),
    transformQux
  )
)

but now transformQux needs to be asynchronous. What's the right way of doing that?

I'm figuring ramda-fantasy futures will be part of the answer
but it's especially confusing in the context of R.over inside this piping.
James Forbes
@JAForbes
Dec 03 2016 04:41
@m59peacemaker pretty interesting scenario. I think if one aspect of the lens is async, then the result is async. Its perfectly valid for that pipeline/lens to return a Future/Task/Stream. I don't see a problem with your sample is what I'm saying.
Johnny Hauser
@m59peacemaker
Dec 03 2016 04:41
Oh neat, I just read a ton of stuff you wrote.
Maybe I was getting close, then.
James Forbes
@JAForbes
Dec 03 2016 04:43
Awesome :D - I'm not a lens expert though, I'm sure someone else around here will pipe in when their timezone avails them :)
Johnny Hauser
@m59peacemaker
Dec 03 2016 04:43
I just realized my sample is missing a bit - would an additional function in the pipeline need to use lift?
James Forbes
@JAForbes
Dec 03 2016 04:43
Is it async as well?
Johnny Hauser
@m59peacemaker
Dec 03 2016 04:43
Nope
James Forbes
@JAForbes
Dec 03 2016 04:44
If the only async step is the last transform, you should be fine throwing multiple non async steps before hand without needing to resort to lift/chain
Johnny Hauser
@m59peacemaker
Dec 03 2016 04:44
Right, but I need to transform it after it is async
James Forbes
@JAForbes
Dec 03 2016 04:45
oh, yeah lift or map
Johnny Hauser
@m59peacemaker
Dec 03 2016 04:45
This is my first day trying to do "real" fp =D
James Forbes
@JAForbes
Dec 03 2016 04:45
I'd probably just use map because its not returning a new Future
Seriously? Your jumping right into lenses? That's pretty impressive
Lenses aren't my strong suit at all
Johnny Hauser
@m59peacemaker
Dec 03 2016 04:47
hah, I doubt that! Lenses seem to be the easiest of all of it. I've been using a few things like that from Ramda for a week or so.
James Forbes
@JAForbes
Dec 03 2016 04:48
How are you finding it so far?
Johnny Hauser
@m59peacemaker
Dec 03 2016 04:48
amazing
James Forbes
@JAForbes
Dec 03 2016 04:48
That's good!
Johnny Hauser
@m59peacemaker
Dec 03 2016 04:48
I have the flaw of absolutely hating a style of programming once I find something better
and this fantasy-land stuff really did me in :D
rm -rf my-project
kidding
James Forbes
@JAForbes
Dec 03 2016 04:49
haha
Johnny Hauser
@m59peacemaker
Dec 03 2016 04:50
Thanks a ton of the good blogs!
James Forbes
@JAForbes
Dec 03 2016 04:50
Its great to hear they were helpful :)
Just thinking about your example. Its a little unusual for a property (e.g. qux) to be a Future, but everything else isn't.
I mean its valid, but probably easier to transform if the entire result is a Future
Johnny Hauser
@m59peacemaker
Dec 03 2016 04:52
Ohhh right
James Forbes
@JAForbes
Dec 03 2016 04:52
Up to you though
Johnny Hauser
@m59peacemaker
Dec 03 2016 04:53
therein lies the difficulty!
Johnny Hauser
@m59peacemaker
Dec 03 2016 04:59
just thinking aloud here, the second function in my pipeline needs to take an object and return a future that represents the object
associating that future with transformQux is the hard part
James Forbes
@JAForbes
Dec 03 2016 05:05
does the third step only process qux, or the entire object?
Johnny Hauser
@m59peacemaker
Dec 03 2016 05:06
the entire object, and it does depend on qux being transformed
James Forbes
@JAForbes
Dec 03 2016 05:08
Here's my non lens approach - but I think we can do better https://runkit.com/jaforbes/58424e3b0878f00013b3bff9
I better get back to work, but I'll check in later. Good luck :)
Johnny Hauser
@m59peacemaker
Dec 03 2016 05:11
@JAForbes It's interesting! Thanks. I'll post if I figure it out with lenses.
James Forbes
@JAForbes
Dec 03 2016 05:13

I just had one idea worth exploring. If each property was a future, maybe you could use R.traverse(Future.of, obj) to turn the obj of futures into a future of an object.

You could easily turn all the properties into futures via R.map(Future.of) but I'm not sure if traverse works on objects, so it might be necessary to turn the object into a list of lists with R.toPairs. Anyway, happy hunting, and looking forward to see what you come up with

Johnny Hauser
@m59peacemaker
Dec 03 2016 06:06
I'm tapped. No luck.
Some sample code, if anyone wants to play with it.
const R = require('ramda')
const Future = require('fluture')

const data = {
  foo: [
    {a: ''},
    {bar: 123, a: ''},
    {bar: 456, a: ''}
  ],
  qux: 'abc'
}

const x = R.pipe(
  R.over(
    R.lensProp('foo'),
    R.filter(R.has('bar'))
  ),
  R.over(
    R.lensProp('qux'),
    () => Future.of('def') // async transform
  )
)(data)

x.fork(
  err => { throw err },
  v => console.log(v) // -> {qux: 'def', foo: [{bar: 123, a: ''}, {bar: 456, a: ''}]}
)
Johnny Hauser
@m59peacemaker
Dec 03 2016 06:18
hah, another thought right after I gave up and started heading to bed
const y = obj => new Future((reject, resolve) => {
  const f = Future.of('def').fork(reject, v => {
    resolve(R.assoc('qux', v, obj))
  })
})
R.pipe(
  R.over(
    R.lensProp('foo'),
    R.filter(R.has('bar'))
  ),
  y
  )
It's awful, but it works
Johnny Hauser
@m59peacemaker
Dec 03 2016 07:50
@JAForbes maybe this?
const R = require('ramda')
const Future = require('fluture')

const data = {
  foo: [{a: ''}, {bar: 123, a: ''}, {bar: 456, a: ''}],
  qux: 'abc'
}

const flattenFuturesObj = (obj) => new Future((reject, resolve) => {
  Object.keys(obj).reduce((acc, key) => {
    let item = obj[key]
    item = Future.isFuture(item) ? item : Future.of(item)
    return acc.chain(result => item.map(v => Object.assign({}, result, {[key]: v})))
  }, Future.of())
  .fork(reject, resolve)
})

R.pipe(
  R.over(R.lensProp('foo'), R.filter(R.has('bar'))),
  R.over(R.lensProp('qux'), () => Future.of('def')),
  flattenFuturesObj
  // ,R.map() // transform moar!
)(data).fork(
  err => { throw err },
  console.log // -> {qux: 'def', foo: [{bar: 123, a: ''}, {bar: 456, a: ''}]}
)
James Forbes
@JAForbes
Dec 03 2016 11:28
@m59peacemaker nice!
Johnny Hauser
@m59peacemaker
Dec 03 2016 18:53
I'm still inclined to think it can be done better
Probably much better. Especially when you consider lensing a path. You'd have to deal with the nesting.
Brad Compton (he/him)
@Bradcomp
Dec 03 2016 19:06
@ram-bot
const isFuture = is(Future);

const toFuture = (arr) => {
  const k = arr[0];
  const v = arr[1];
  if (isFuture(v)) return v.map(val => [k, val]);
  return Future.of(arr);
};

const obj = {
  foo: 1,
  bar: 'abc',
  qux: Future.of('qux')
}

pipe(
  toPairs,
  map(toFuture),
  sequence(Future.of),
  map(fromPairs)
)(obj).fork(console.log, console.log)
ram-bot
@ram-bot
Dec 03 2016 19:06
console is not defined
Brad Compton (he/him)
@Bradcomp
Dec 03 2016 19:06
whoops!
No lens, but a way to convert an object where some of the items are Futures into a future containing the object
Johnny Hauser
@m59peacemaker
Dec 03 2016 19:08
Thanks! I'll try to absorb that! The thought also crossed my mind, is there any merit to the idea of an async version of over and set?
(lens, mapper, value) and returns a future representing the future value of mapper ?
Brad Compton (he/him)
@Bradcomp
Dec 03 2016 19:13

It may be possible to do something like that with the second type sig of over:

Lens s a = Functor f => (a → f a) → s → f s

Doesn't look like that works automatically though :-(

Johnny Hauser
@m59peacemaker
Dec 03 2016 19:13
I'm not quite skilled enough to understand that Haskell-y stuff yet =D
I've been trying.
Brad Compton (he/him)
@Bradcomp
Dec 03 2016 19:17

A Functor is an object with a map method.

It says: Given a lens for structure s focused on a value of type a , and a function from a that returns a Functor containing a it will return a Functor containing the structure s

@ram-bot
over(lensIndex(0), of, [1, 2, 3])
ram-bot
@ram-bot
Dec 03 2016 19:18
[ [ 1 ], 2, 3 ]
Brad Compton (he/him)
@Bradcomp
Dec 03 2016 19:19
It doesn't look like the second signature reflects the behavior of the function though
Johnny Hauser
@m59peacemaker
Dec 03 2016 19:20
The documentation is wrong, or it's a bug, then?
Brad Compton (he/him)
@Bradcomp
Dec 03 2016 19:20
Not sure, was gonna open an issue
Johnny Hauser
@m59peacemaker
Dec 03 2016 19:20
I'm not following on the part where you say "a function from a"
Brad Compton (he/him)
@Bradcomp
Dec 03 2016 19:21
It could also be my understanding that is wrong, but the type signature is clear
Johnny Hauser
@m59peacemaker
Dec 03 2016 19:21
oh, as in, a function that takes a and returns a Functor containing it?
Brad Compton (he/him)
@Bradcomp
Dec 03 2016 19:21

length :: String -> Int

is a function from String to Int

Exactly!
Johnny Hauser
@m59peacemaker
Dec 03 2016 19:22
Clicked now, sweet.
is there a resource for mastering reading those?
I've read a few things that didn't help.
but based on what you just explained, it seems to me like it works correctly
Also see issue I just opened :point_right:
Brad Compton (he/him)
@Bradcomp
Dec 03 2016 19:33
@ram-bot

const qLens = lensProp('qux');
const obj = {qux: 1};

pipe(view(qLens), Maybe.of, map(set(qLens, __, obj)))(obj)
ram-bot
@ram-bot
Dec 03 2016 19:33
_Just { value: { qux: 1 } }
Brad Compton (he/him)
@Bradcomp
Dec 03 2016 19:34
So by separating out the view from the set we can map over the Functor to set the value inside. That's pretty neat!
Johnny Hauser
@m59peacemaker
Dec 03 2016 19:37
that is sorcery
Brad Compton (he/him)
@Bradcomp
Dec 03 2016 19:39
The cool thing about that code is that, because it just relies on the Functor typeclass, it will work just as well for functions returning Futures or Eithers or whatevers
Johnny Hauser
@m59peacemaker
Dec 03 2016 19:40
I'm sure it will be a total heartmelter to me once I understand how it is doing that
Brad Compton (he/him)
@Bradcomp
Dec 03 2016 19:40
I can try to explain if you're interested
Johnny Hauser
@m59peacemaker
Dec 03 2016 19:41
would it be ok if I try to talk it out first?
Brad Compton (he/him)
@Bradcomp
Dec 03 2016 19:41
Yes! I didn't want to ruin it unless you wanted
Johnny Hauser
@m59peacemaker
Dec 03 2016 19:42
I can't get passed the view dernit
If takes a lens and returns the value of it
Oh
OH
Brad Compton (he/him)
@Bradcomp
Dec 03 2016 19:42
:-D
Johnny Hauser
@m59peacemaker
Dec 03 2016 19:43
view(qLens, obj) => value of qux (1)
Maybe.of(1)
set(qLens, __, obj) set qux to the object itself, which is like Maybe.of(object)
and the map is to actually set the value of qux
Brad Compton (he/him)
@Bradcomp
Dec 03 2016 19:44
:+1:
Johnny Hauser
@m59peacemaker
Dec 03 2016 19:45
It's a touch impure though
Brad Compton (he/him)
@Bradcomp
Dec 03 2016 19:45
Yes, I agree
It was a quick sketch, you would want to wrap it up in a function
function over_(lens, f, obj) {
   var result = f(view(obj));
  return map(val => set(lens, val, obj), result)
}
Johnny Hauser
@m59peacemaker
Dec 03 2016 20:24
That would be var result = f(view(lens, obj));
Johnny Hauser
@m59peacemaker
Dec 03 2016 20:32
I fried my brain but I'm learning things =D
Brad Compton (he/him)
@Bradcomp
Dec 03 2016 20:37
Yeah, sorry!
I wrote it out without trying it :stuck_out_tongue:
Brad Compton (he/him)
@Bradcomp
Dec 03 2016 20:55
@m59peacemaker If you haven't already seen it, I can highly recommend Professor Frisby's Mostly Adequate Guide: https://drboolean.gitbooks.io/mostly-adequate-guide/content/
Johnny Hauser
@m59peacemaker
Dec 03 2016 21:20
thanks!
I wrote this out to help me
const over = R.curry((lens, f, v) => R.map( // map over the functor
  // "newVal" is the value contained in the functor
  newVal => R.set(lens, newVal, v), // the object with the lens set to the new value
  f(R.view(lens, v)) // functor containing new value
)
It's crazy how actually simple that is once you get it
Brad Compton (he/him)
@Bradcomp
Dec 03 2016 21:29
yep!
We take the little pieces and put them together into bigger pieces, but it's just functions all the way down.
Johnny Hauser
@m59peacemaker
Dec 03 2016 21:30
I'm surprised that formula isn't insanely common
I mean, in that this isn't something where everyone doing FP in javascript just says, "oh use {{function name}} like so" for that
but perhaps Ramda is meant to have it built in, like you said
James Forbes
@JAForbes
Dec 03 2016 23:34
@Bradcomp Your example is exactly what I was thinking but I couldn't get it to work :D. Makes me wonder, could objects be treated as applicative functors in ramda? That way we could skip the toPairs,fromPairs step and just go: R.sequence(Future.of, objectOfFutures) //=> Future object
Hardy Jones
@joneshf
Dec 03 2016 23:37
The future would need to be Applicative, while the object Traversable.
Brad Compton (he/him)
@Bradcomp
Dec 03 2016 23:37
@JAForbes @joneshf exactly! And traversable means ordered
And objects in JS aren't ordered.
James Forbes
@JAForbes
Dec 03 2016 23:38
I thought they were in es6?
Hardy Jones
@joneshf
Dec 03 2016 23:38
I don't think you need the data type to be ordered.
Brad Compton (he/him)
@Bradcomp
Dec 03 2016 23:39
To be traversable?
Hardy Jones
@joneshf
Dec 03 2016 23:39
Yeah.
Brad Compton (he/him)
@Bradcomp
Dec 03 2016 23:40
Huh...
James Forbes
@JAForbes
Dec 03 2016 23:40
map works on objects in ramda so it seems weird that its not considered a functor by other functions
Brad Compton (he/him)
@Bradcomp
Dec 03 2016 23:41
@ram-bot
Object.keys({a: 1, b: 2})
ram-bot
@ram-bot
Dec 03 2016 23:41
[ 'a', 'b' ]
Brad Compton (he/him)
@Bradcomp
Dec 03 2016 23:41
@ram-bot
Object.keys({b: 2, a: 1})
ram-bot
@ram-bot
Dec 03 2016 23:41
[ 'b', 'a' ]
Brad Compton (he/him)
@Bradcomp
Dec 03 2016 23:41
@ram-bot
equals({a:1, b: 2}, {b: 2, a: 1})
ram-bot
@ram-bot
Dec 03 2016 23:41
true
James Forbes
@JAForbes
Dec 03 2016 23:42
Yeah that's interesting @Bradcomp
Brad Compton (he/him)
@Bradcomp
Dec 03 2016 23:42
@ram-bot
equals(keys({a:1, b: 2}), keys({b: 2, a: 1}))
ram-bot
@ram-bot
Dec 03 2016 23:42
false
Brad Compton (he/him)
@Bradcomp
Dec 03 2016 23:42
So maybe not directly a problem for traversible, but definitely an issue
James Forbes
@JAForbes
Dec 03 2016 23:42
is the behaviour of equals the issue though?
Hardy Jones
@joneshf
Dec 03 2016 23:43
I mean, so long as you satisfy the laws, it doesn't really matter how the data type is implemented. And I don't think you need the ordering to be defined for objects in order to satisfy the laws.
Brad Compton (he/him)
@Bradcomp
Dec 03 2016 23:43
equals(a, b) should imply equals(f(a), f(b))
James Forbes
@JAForbes
Dec 03 2016 23:43
The js engine treats those two objects as different hidden classes
I mean thats not really relevant at all, but just making the case, maybe equals is wrong :D
Brad Compton (he/him)
@Bradcomp
Dec 03 2016 23:45
I mean, I guess you could say it's an issue with keys too ;)
yeah keys, in the spec is unordered, but in every major env is ordered
Hardy Jones
@joneshf
Dec 03 2016 23:46

equals(a, b) should imply equals(f(a), f(b))

Is that true for ramda though?

James Forbes
@JAForbes
Dec 03 2016 23:46
I guess I'm just saying there is a case to be made, and I think it would be useful to make objects traversable, but then @joneshf is saying order doesn't matter anyway so maybe this is neither here nor there
Brad Compton (he/him)
@Bradcomp
Dec 03 2016 23:46
But, back to the relevant point. I think the reason that reduce et. al. hasn't been implemented in Ramda for objects is related to this. reduce(concat, {a: [1], b: [2]}) depends on the order in which the object is defined

Is that true for ramda though?

I just proved it wasn't true

Hardy Jones
@joneshf
Dec 03 2016 23:47
:)
Brad Compton (he/him)
@Bradcomp
Dec 03 2016 23:47
@JAForbes I agree, it would be totally useful
I just seem to recall some issue comments pointing out the issue I'm raising
James Forbes
@JAForbes
Dec 03 2016 23:48
ramda lives in its own version of javascript, I'm wondering if this warrants expanding ramda's imaginary version of javascript a tiny bit
I mean toPairs/fromPairs is almost admitting we see them as ordered anyway
Hardy Jones
@joneshf
Dec 03 2016 23:49
That seems solvable though.
If the ordering is a concern for ramda, couldn't the keys be sorted?
Brad Compton (he/him)
@Bradcomp
Dec 03 2016 23:50
Seems like a great opportunity for a Static Land library :-D
Hardy Jones
@joneshf
Dec 03 2016 23:50
In the implementation.
James Forbes
@JAForbes
Dec 03 2016 23:50
Yeah I'm sure there is a good reason not to do it, it struck me when I saw what @m59peacemaker was trying to do how much simpler it would be if objects were treated as functors
Brad Compton (he/him)
@Bradcomp
Dec 03 2016 23:50

If the ordering is a concern for ramda, couldn't the keys be sorted?

Yeah, that would be the obvious solution

Hardy Jones
@joneshf
Dec 03 2016 23:50
Yeah, exactly, just throw away objects entirely!
James Forbes
@JAForbes
Dec 03 2016 23:50

Seems like a great opportunity for a Static Land library

Yeah maybe

Hardy Jones
@joneshf
Dec 03 2016 23:50
They're less than ideal little creatures.
specifically because of things like this.
Is the ordering of Map defined?
James Forbes
@JAForbes
Dec 03 2016 23:51
Yeah I think so @joneshf
Hardy Jones
@joneshf
Dec 03 2016 23:51
Shoot, why not use that all the time then?
I mean, when we're treating objecs as maps.
Not when we're treating them as records.
A Map object iterates its elements in insertion order — a for...of loop returns an array of [key, value] for each iteration.
Brad Compton (he/him)
@Bradcomp
Dec 03 2016 23:52
:point_up: That's ... disappointing
James Forbes
@JAForbes
Dec 03 2016 23:53
the key/val? @Bradcomp
Hardy Jones
@joneshf
Dec 03 2016 23:53
ugh
So scrap that as well
Brad Compton (he/him)
@Bradcomp
Dec 03 2016 23:53
insertion order
James Forbes
@JAForbes
Dec 03 2016 23:54
Oh right, I thought that was a good thing :)
Brad Compton (he/him)
@Bradcomp
Dec 03 2016 23:54
It could be viewed as good, but my issue is that it means that the order is dependent on how it was defined, rather than anything inherent to the data structure
James Forbes
@JAForbes
Dec 03 2016 23:55
Yeah I see now
I was thinking "at least its defined"
Brad Compton (he/him)
@Bradcomp
Dec 03 2016 23:55
So like, a trie (or tree) based map would iterate in the same order, regardless of the order of insertion
Hardy Jones
@joneshf
Dec 03 2016 23:55
New plan! Write a wrapper around purescript-maps that makes it not a PITA to use from jsland and just use that.
Brad Compton (he/him)
@Bradcomp
Dec 03 2016 23:55
:-D
James Forbes
@JAForbes
Dec 03 2016 23:55
:)
Brad Compton (he/him)
@Bradcomp
Dec 03 2016 23:56
Don't get me wrong, I love the new Map and Set.
James Forbes
@JAForbes
Dec 03 2016 23:56
So just so we're clear, sorting keys and treating objects as functors/traversable is off the table?
Brad Compton (he/him)
@Bradcomp
Dec 03 2016 23:56
Not for me!
But I think it might be for Ramda IIRC
Hardy Jones
@joneshf
Dec 03 2016 23:56
Same ^
James Forbes
@JAForbes
Dec 03 2016 23:56
I think if we are explicit about how ramda determines equality it would be ok
Brad Compton (he/him)
@Bradcomp
Dec 03 2016 23:56
I don't know though, just recalling what I've read
Hardy Jones
@joneshf
Dec 03 2016 23:57
I'm not maintaining it so I can't comment.
Brad Compton (he/him)
@Bradcomp
Dec 03 2016 23:57
Me neither
Going the other way
Also, woefully out of date.
Brad Compton (he/him)
@Bradcomp
Dec 03 2016 23:59
ramda/ramda#1601
Hardy Jones
@joneshf
Dec 03 2016 23:59
Or wait, that is the right way.