These are chat archives for ramda/ramda

8th
Mar 2016
Constantin Dumitrescu
@dumconstantin
Mar 08 2016 08:28
Hi all, can anyone suggest some way of documenting FP code in JS?
I'm trying out JSDoc but using OOP jargon make things ambiguous (how would you define curried functions, compositions, etc?)
Keith Alexander
@kwijibo
Mar 08 2016 08:33
@dumconstantin good q. I think from a documentation POV it doesn't matter if you're creating the function with composition or a function declaration or expression
David Chambers
@davidchambers
Mar 08 2016 08:41
@dumconstantin, check out Transcribe.
Constantin Dumitrescu
@dumconstantin
Mar 08 2016 08:46
thank you, looking at the links now
Constantin Dumitrescu
@dumconstantin
Mar 08 2016 09:01
very nice overview of type signatures, I'll make sure I'll add them!
just downloaded Transcribe and played around with it for a bit, very lightweight and from the source it seems very easy to extend
Constantin Dumitrescu
@dumconstantin
Mar 08 2016 09:08
atm I need to do docs for a project that uses Kefir, Ramda, BaobabJS and Riot which together seem a bit complicated to document properly...
but a solid documentation engine for FP code in JS is currently severely lacking, maybe Transcribe can get there? Don't know what PureScript/Elm folks are doing in this direction...
Keith Alexander
@kwijibo
Mar 08 2016 11:24
does Ramda have an index equivalent to R.prop?
Raine Virta
@raine
Mar 08 2016 11:24
nth
Keith Alexander
@kwijibo
Mar 08 2016 11:25
thanks @raine
Risto Stevcev
@Risto-Stevcev
Mar 08 2016 11:57
Is there any way to extend a lens path?
Keith Alexander
@kwijibo
Mar 08 2016 11:58
@Risto-Stevcev compose it with another one?
Risto Stevcev
@Risto-Stevcev
Mar 08 2016 11:58
For ex if you had something like R.lensPath['a', 'b'], could you use this lens to get the path R.lensPath['a', 'b', 'c']?
Ah, thanks @kwijibo :)
Keith Alexander
@kwijibo
Mar 08 2016 11:59
R.compose(abLens, R.lensProp('c')) (note that lenses compose like transducers, not like getters)
(ie use compose instead of pipe to build a L->R path)
Risto Stevcev
@Risto-Stevcev
Mar 08 2016 12:01
@ram-bot
R.view(R.compose(R.lensPath(['a', 'b']), R.lensPath(['c'])), { a: { b: { c: 'foo' } } })
ram-bot
@ram-bot
Mar 08 2016 12:01
'foo'
Risto Stevcev
@Risto-Stevcev
Mar 08 2016 12:01
gotcha
@ram-bot
R.view(R.compose(R.lensPath(['foo, 'bar']), R.lensProp('baz')), { foo: { bar: { baz: 'qux' } } })
ram-bot
@ram-bot
Mar 08 2016 12:04
SyntaxError: Unexpected identifier
Risto Stevcev
@Risto-Stevcev
Mar 08 2016 12:04
oops
shouldnt code tired
@ram-bot
R.view(R.compose(R.lensPath(['foo', 'bar']), R.lensProp('baz')), { foo: { bar: { baz: 'qux' } } })
ram-bot
@ram-bot
Mar 08 2016 12:04
'qux'
Aldwin Vlasblom
@Avaq
Mar 08 2016 12:56
Hello everyone! I'm creating a Future library which aims to provide better error messages, better speed and "the complete package". Currently it's faster than data.task and much faster than ramda-fantasy.Future and Promise. It also provides very clear error messages in my opinion. I'm working hard on making it complete by adding a bunch of Future related utilities that I often use in my daily work. I would love for you skeptics to take a close look, maybe even use it, and tell me what you think: https://github.com/Avaq/Fluture :)
Keith Alexander
@kwijibo
Mar 08 2016 12:58
@Avaq is it just the npm install part that requires node.js v5? I presume it works in browsers etc too?
Aldwin Vlasblom
@Avaq
Mar 08 2016 12:58
It runs in Node 5.0.0 or a compatible environment like modern browsers or transpilers.
I've put the note there to show I haven't done any compiling myself. The source you install is "node 5.0.0 compatible ES6".
Aldwin Vlasblom
@Avaq
Mar 08 2016 13:04
If the demand arises, I might make an es5 branch where I compile with Babel, and publish that as fluture-es5 or something. I wouldn't be able to guarantee that the same code optimizations are in that version though.
Keith Alexander
@kwijibo
Mar 08 2016 13:11
cool
can you give an example of the better error handling/messages?
Aldwin Vlasblom
@Avaq
Mar 08 2016 13:12
> Future.of('hello').chain(() => 'world').fork(console.error, console.log)
TypeError: Future#chain expects the function its given to return a Future
  Actual: "world"
  From calling: () => 'world'
  With: "hello"
    at check$chain$f
    at Future$chain$res
    at Object.Future$of$fork [as _f]
    at Object.Future$chain$fork [as _f]
    at Object.Future$fork [as fork]
(The exception is thrown, not propagated down the reject track)
Keith Alexander
@kwijibo
Mar 08 2016 13:14
very nice
David Chambers
@davidchambers
Mar 08 2016 15:25
Very nice indeed.
David Chambers
@davidchambers
Mar 08 2016 16:35
Another must-read comment by @joneshf. Thanks, Hardy!
Keith Alexander
@kwijibo
Mar 08 2016 17:01
+1
LeonineKing1199
@LeonineKing1199
Mar 08 2016 17:11
I'm wondering if Promise has ever been the actual bottleneck of an application before...
I'm more prone to thinking that it's what is in the Promise chains that make them slow rather than the implementation of the Promise itself which largely just seems to be syntactic sugar for attaching callbacks to asynchronous functions
Keith Alexander
@kwijibo
Mar 08 2016 17:26
I've been told that if you use Promise.then as you'd use Future.map, to compose synchronous functions, there's a performance hit because each .then callback happens on the nextTick of the event loop. But I don't know if that's either true or significant compared to network latency etc.
Aldwin Vlasblom
@Avaq
Mar 08 2016 17:35

Promises have to do a lot of checking and caching to be able to work the way they do. Promises are designed to be eager, which means they try to call their inner function as soon as they can. This has several negative consequences for performance:

  • They have to cache the result from the computation, because they don't know when the callback will come
  • They have to cache all callbacks, because they don't know when the result will come

Here's the result from this benchmark:

Fluture x 48,333,389 ops/sec ±0.84% (88 runs sampled)
data.task x 29,160,160 ops/sec ±1.12% (89 runs sampled)
Ramda Fantasy x 18,465,285 ops/sec ±0.79% (89 runs sampled)
Promise x 2,847,998 ops/sec ±0.52% (94 runs sampled)

To give an idea of everything they have to do which lose them performance, take a look at Future.cache. Futures don't need to do caching and such by default, because they're not eager, they can be made to function like Promises though, using that function. So it's a good way to see everything a Promise does by default, which a Future doesn't have to do.

Then there's the issue Promises being "smart". They inspect your return value to determine whether to act like map or act like chain. All this checking costs them as well.
LeonineKing1199
@LeonineKing1199
Mar 08 2016 17:40
That benchmark almost seems a little too synthetic. Mainly instantiating a new Promise and having it immediately resolve to a synchronous value.
Better performance gains might be seen from reducing database and AJAX calls
Aldwin Vlasblom
@Avaq
Mar 08 2016 17:45
Of course! :)
I'm no advocate of micro benchmarking by any stretch. Chances are that you're creating a handful of Promises in your application, and the main bottlenecks are nowhere near the performance lost by instantiating them. I'm mainly just doing this for exercise. I wanted to create a Future library and see how far I can push performance, with the added benefit of not having people question the performance because it gives good errors. ;)
LeonineKing1199
@LeonineKing1199
Mar 08 2016 17:46
Yeah, and I wasn't trying to knock what you were doing. Believe me, I love faster everything. :)
The Fluture implementation does seem to be quite significantly faster
Risto Stevcev
@Risto-Stevcev
Mar 08 2016 18:12
@ram-bot
R.view(R.compose(R.lensProp('baz'), R.lensPath(['foo', 'bar'])), { foo: { bar: { baz: 'qux' } } })
ram-bot
@ram-bot
Mar 08 2016 18:12
undefined
Risto Stevcev
@Risto-Stevcev
Mar 08 2016 18:12
@ram-bot
R.view(R.compose(R.lensProp('baz'), R.lensPath(['foo', 'bar'])), { baz: { foo: { bar: 'qux' } } })
ram-bot
@ram-bot
Mar 08 2016 18:12
'qux'
LeonineKing1199
@LeonineKing1199
Mar 08 2016 18:15
THERE'S A REPL!!!!
LeonineKing1199
@LeonineKing1199
Mar 08 2016 18:20
Man, lenses are weird... Why not just use R.path()?
Risto Stevcev
@Risto-Stevcev
Mar 08 2016 18:56
true
because if a path like foo.bar.baz doesn't exist and foo = {} it'll create the path
and plus R.view(lensQux, foo) is arguably more readable than foo[bar][baz][qux]
@ram-bot
R.set(R.lensPath(['x','y','z']), 'hi', {})
ram-bot
@ram-bot
Mar 08 2016 18:59
{ x: { y: { z: 'hi' } } }
Brad Compton (he/him)
@Bradcomp
Mar 08 2016 19:01
@ram-bot
R.assocPath(['a', 'b', 'c'], 'hi', {})
ram-bot
@ram-bot
Mar 08 2016 19:01
{ a: { b: { c: 'hi' } } }
Risto Stevcev
@Risto-Stevcev
Mar 08 2016 19:07
@Bradcomp Hmm... I guess you could use assocPath. I'm wondering what you would use lenses for then
Brad Compton (he/him)
@Bradcomp
Mar 08 2016 19:07
@Risto-Stevcev So am I
Risto Stevcev
@Risto-Stevcev
Mar 08 2016 19:08
it seems more like something haskell did because it sucks at doing that kind of stuff? I don't know too much about the whole story behind them
But I do know that haskell doesn't support row polymorphism
Which is really dumb
Brad Compton (he/him)
@Bradcomp
Mar 08 2016 19:10
Lenses have been a piece of Ramda I have yet to find a use for in my code, though I know there are people who use them a lot.
Maybe it's because I'm coming from JS to FP, instead of the other way around though. I dunno
LeonineKing1199
@LeonineKing1199
Mar 08 2016 19:12
#ThatCouldHaveBeenOneLineInHaskell
Risto Stevcev
@Risto-Stevcev
Mar 08 2016 19:17
assocPath creates a shallow clone of the object
not quite the same as set
I hope someone more knowledgeable can answer that
Brad Compton (he/him)
@Bradcomp
Mar 08 2016 19:27
Ah, that makes sense
Risto Stevcev
@Risto-Stevcev
Mar 08 2016 19:43
@ram-bot
var xLens = R.lensProp('x');
var obj = {x: 1, y: 2};
R.set(xLens, 4, obj);
console.log(obj);
ram-bot
@ram-bot
Mar 08 2016 19:43
ReferenceError: console is not defined
Risto Stevcev
@Risto-Stevcev
Mar 08 2016 19:43
@ram-bot
var xLens = R.lensProp('x');
var obj = {x: 1, y: 2};
R.set(xLens, 4, obj);
obj
ram-bot
@ram-bot
Mar 08 2016 19:43
{ x: 1, y: 2 }
Risto Stevcev
@Risto-Stevcev
Mar 08 2016 19:43
Nope
set has the same behavior as assocPath
Brad Compton (he/him)
@Bradcomp
Mar 08 2016 19:50
Lenses can work on more than just paths and props though, right?
Risto Stevcev
@Risto-Stevcev
Mar 08 2016 19:58
The signature seems to suggest it: (s → a) → ((a, s) → s) → Lens s a
But I'm wondering what would be a use case
Aadi Deshpande
@cilquirm
Mar 08 2016 20:41
hi, would this be an appropriate place to ask about ramda-fantasy and ramda-lens?
David Chambers
@davidchambers
Mar 08 2016 21:25
Absolutely, @cilquirm. :)
Aadi Deshpande
@cilquirm
Mar 08 2016 21:28
@davidchambers thanks! i'm trying to use ramda-fantasy, and ramda-lens together to extract data from a large immutable js document.
David Chambers
@davidchambers
Mar 08 2016 21:28

Lenses can work on more than just paths and props though, right?

Yes. One example is a temperate in Celsius which one wishes to read in Fahrenheit. One could create a lens from one form to the other. One could then read and write the value in Fahrenheit, if desired, without changing the underlying representation.

Aadi Deshpande
@cilquirm
Mar 08 2016 21:29
in my document, there are primary values and fallback values . what's the proper way to create a lens that can query for primary values and fall back to fallback values?
right now i'm doing Maybe(L.view(primary,doc)).getOrElse(L.view(fallback,doc))
where L. is ramda-lens and Maybe from ramda-fantasy
primary and fallback are basically lensProps ( customized for immutableJS )
Brad Compton (he/him)
@Bradcomp
Mar 08 2016 21:43

Yes. One example is a temperate in Celsius which one wishes to read in Fahrenheit. One could create a lens from one form to the other. One could then read and write the value in Fahrenheit, if desired, without changing the underlying representation.

Thats cool!

David Chambers
@davidchambers
Mar 08 2016 21:56
@cilquirm, are you using Maybe(L.view(primary, doc)) because L.view(primary, doc) may evaluate to null/undefined?
Scott Sauyet
@CrossEye
Mar 08 2016 21:59
has Ramda-Lens actually diverged significantly from R.lens yet? I know there are plans...
Aadi Deshpande
@cilquirm
Mar 08 2016 22:01
@davidchambers yes
Risto Stevcev
@Risto-Stevcev
Mar 08 2016 22:09
I'd like to make a suggestion if something similar doesn't already exist
Scott Sauyet
@CrossEye
Mar 08 2016 22:10
yes...?
Risto Stevcev
@Risto-Stevcev
Mar 08 2016 22:10
// [String] → (a -> b) → {k: v} → {k: v}
R.mapPath(['a', 'b', 'c'], R.add(40), {a: {b: {c: 2}}}); //=> {a: {b: {c: 42}}}
David Chambers
@davidchambers
Mar 08 2016 22:14
Your code looks fine to me, @cilquirm. S.get and S.gets are more precise ways of accessing properties of inconsistent objects, but if you know the value will always be of type Nullable Blah, Maybe :: a? -> Maybe a should be fine.
This message was deleted
Scott Sauyet
@CrossEye
Mar 08 2016 22:16
@ram-bot R.over(R.lensPath(['a', 'b', 'c']), R.add(40), {a: {b: {c: 2}}})
ram-bot
@ram-bot
Mar 08 2016 22:16
{ a: { b: { c: 42 } } }
David Chambers
@davidchambers
Mar 08 2016 22:17
My thoughts exactly, @CrossEye!
We already have a number of functions which duplicate functionality provided by lenses. Let's not add more!
Scott Sauyet
@CrossEye
Mar 08 2016 22:18
To be fair, we have a lot of functions that predated the lens implementation.
David Chambers
@davidchambers
Mar 08 2016 22:18
Absolutely.
Scott Sauyet
@CrossEye
Mar 08 2016 22:19
But yes, I don't see much reason to add more.
David Chambers
@davidchambers
Mar 08 2016 22:19
:)
Aadi Deshpande
@cilquirm
Mar 08 2016 22:20
@davidchambers i wasn't familiar with Sanctuary. would you recommend it over ramda-fantasy?
David Chambers
@davidchambers
Mar 08 2016 22:20
It depends on your needs. Sanctuary currently only provides Maybe and Either.
I will comment on ramda/ramda-fantasy#105 soon. ;)
Risto Stevcev
@Risto-Stevcev
Mar 08 2016 22:21
@CrossEye Thanks for the example
Another reason to use lenses
@cilquirm sanctuary is really nice, but ramda-fantasy is too. ramda-fantasy has the IO monad, for example. I tend to use sanctuary + ramda
Aadi Deshpande
@cilquirm
Mar 08 2016 22:29
tbh, i really picked ramda-fantasy because it looked and worked more like Monet and Scala's Option.
i can't even figure out how to extract the value from sanctuary's Maybe
Risto Stevcev
@Risto-Stevcev
Mar 08 2016 22:33
Btw, is there a way to view the value of a lensPath?
David Chambers
@davidchambers
Mar 08 2016 22:36

i can't even figure out how to extract the value from sanctuary's Maybe

You can use fromMaybe :: a -> Maybe a -> a or maybe :: b -> (a -> b) -> Maybe a -> b.

maybe is just sugar for a combination of map and fromMaybe.
Brad Compton (he/him)
@Bradcomp
Mar 08 2016 22:38
@ram-bot R.view(R.lensPath(['a', 'b', 'c']), {a: {b: {c: 2}}})
ram-bot
@ram-bot
Mar 08 2016 22:38
2
Risto Stevcev
@Risto-Stevcev
Mar 08 2016 23:00
I'm looking for something like reading R.lensPath(['a', 'b', 'c'])to return ['a', 'b', 'c'], which would be useful for debugging
Brad Compton (he/him)
@Bradcomp
Mar 08 2016 23:04
Ooooh, gotcha.
Scott Sauyet
@CrossEye
Mar 08 2016 23:40
@Risto-Stevcev: ,No, nothing like that, I'm afraid. It's hard to figure out how that would work, too. What if you create a Lens by composing a lensIndex with a lensProp with a lensPath with a celsiusFarenheitLens? What sort of output would you expect from its toString?
Risto Stevcev
@Risto-Stevcev
Mar 08 2016 23:40
Yeah I was thinking that
oh well
since lensIndex, lensProp and lensPath all have the same getter/setter, you could have a function that either returns the getter path or the setter path
that way if it's composed the expected output wouldn't be ambiguous
Scott Sauyet
@CrossEye
Mar 08 2016 23:43
@cilquirm: There's an argument to be made that if you're trying to extract the value from a Maybe, perhaps you're using it wrong. Mostly, you map or chain over them, perhaps ap them. I'm not saying it's universally bad, but the point is that you lose much of the type-safety gained by using a Maybe if you're going to extract its value.
@Risto-Stevcev: Not sure what you mean. nth(x), prop(x), and path(x) are the getters. The setters vary similarly.
Risto Stevcev
@Risto-Stevcev
Mar 08 2016 23:55
Something like,
R.compose(lensIndex(1), lensProp('x'), lensPath(['y', 'z']))   // would return [1, 'x', 'y', 'z']
for the function that would be able to print the lens
Ah I see what you're saying, it wouldn't be trivial
Scott Sauyet
@CrossEye
Mar 08 2016 23:58
Right, but it would be quite tricky. How does compose know that it's creating a lens?
exactly
Risto Stevcev
@Risto-Stevcev
Mar 08 2016 23:59
Maybe there could be a hackish sort of way of doing it