These are chat archives for ramda/ramda

8th
Sep 2016
LeonineKing1199
@LeonineKing1199
Sep 08 2016 00:05
Sets are iterated in insertion order
Maps seem to be as well
Kurt Milam
@kurtmilam
Sep 08 2016 00:16
Any idea why the following doesn't work / how I can approach this problem?
converge(
  ifElse, 
  [
    prop('test')
    , always('yep')
    , prop('nope')
  ]
)({test:true, nope:'nope'})
Brad Compton (he/him)
@Bradcomp
Sep 08 2016 00:21
@ram-bot
ifElse(prop('test'), always('yep'), prop('nope'))({test: true, nope: 'nope'})
ram-bot
@ram-bot
Sep 08 2016 00:21
'yep'
Brad Compton (he/him)
@Bradcomp
Sep 08 2016 00:22
@kurtmilam You don't need converge because ifElse takes functions instead of values.
Kurt Milam
@kurtmilam
Sep 08 2016 00:29
@Bradcomp Thanks again. It looks like I need to spend a little more time getting used to and paying attention to the signatures.
Rafe
@rjmk
Sep 08 2016 00:31
@dtipson I guess it comes down to how much you think of compose as just a specialisation of mconcat. It can't just be that as there's only a monoid (hence, empty element) for endomorphisms (where the domain and range of a function are the same) and not for arbitrary functions (as far as I'm aware)
Drew
@dtipson
Sep 08 2016 01:27
right, I'm not sure there's a good direct argument to be had there (from Endo, thus to compose), just one way the intuition could work. On the other hand, if a variadic compose() should throw an error for no arguments because it's trying to mirror f=>g=>x = f(g(x)) and up, then why shouldn't it throw an error for only one argument as well? f=>g=>x=f(g(x)) is just as broken when g is undefined as when both g and f are, and composing a single function in a variadic compose is equally weird as none, in soe ways. I don't have, or know if there is a right answer. I'm curious if it throws an error in Ramda for reasons related to internal usage/needs, or if there's an larger overriding reason in Ramda's ethic for doing it that way.
Rafe
@rjmk
Sep 08 2016 09:09

then why shouldn't it throw an error for only one argument as well?

Yeah, very good point!

Kurt Milam
@kurtmilam
Sep 08 2016 10:09

I'm having a difficult time figuring out why this works:

is(String)(valM)
  ? compose(prop('body'), Handlebars.parse)(valM)
  : always([])(valM)

But this doesn't:

ifElse(
  is(String)
  , compose(prop('body'), Handlebars.parse)
  , always([])
)(valM)
Also, in general, I'm having a bit of a rough time debugging code written with Ramda that doesn't work as expected.
Any tips there?
Rafe
@rjmk
Sep 08 2016 10:17
@kurtmilam The latter works fine for me. In general, I find the key is to start with the smallest possible example (both in terms of shorter compositions and more trivial elements of the composition) and then to work back towards what I started with, debugging as I go
Where there are equivalent functions in Sanctuary to Ramda, sometimes dropping them in can yield cleaner error messages
Kurt Milam
@kurtmilam
Sep 08 2016 10:17
Lastly (for now), is anyone aware of any good articles or resources that might help someone who'd like to switch from a more imperative coding style to a point free functional style?
@rjmk while the latter may seem to work fine, it's definitely returning different output than the former, using the same input in both cases (at least for me)
when I tap the former, I get the output I would expect to see. When I tap the latter, I get [Function]
Rafe
@rjmk
Sep 08 2016 10:24
Here's what I'm playing with: http://goo.gl/t1qWrq
Kurt Milam
@kurtmilam
Sep 08 2016 10:44
I'm stumped. I copied and pasted the code unaltered from my program, and while it seems to work in the REPL, it doesn't work in the application. Here's a slightly modified version of your REPL creating a 'stub' Handlebars.parse method. Works in REPL, doesn't work in my application.
Kurt Milam
@kurtmilam
Sep 08 2016 11:04
In my code, the ifElse function is never called when valM is a string, so the result is always a [Function].
James Forbes
@JAForbes
Sep 08 2016 11:22
@kurtmilam can you post your application code verbatim into the repl? Even though it will be errored. We might be able to spot something
Rafe
@rjmk
Sep 08 2016 11:33
@kurtmilam Can Handlebars.parse take more than one argument?
You could try wrapping it like x => Handlebars.parse(x) or curryN(1, Handlebars.parse) (or there may be something else in Ramda's API)
Keith Alexander
@kwijibo
Sep 08 2016 11:35
Does anyone use Reader monad for dependency injection in practice? Seems like the common practice is to lump all the dependencies into a single env object that gets passed to the Reader - but how do you keep track of what all the dependencies are that need to be in env ?
Kurt Milam
@kurtmilam
Sep 08 2016 12:04
Here's the verbatim code: http://goo.gl/hFdSsG
I don't think the problem is with Handlebars.parse. First, it works in the ternary operator example but doesn't in the ifElse example. Second, when I replace the is(String) with compose(is(String), tap(console.log)), the string input is logged in the ternary operator example but nothing is logged in the ifElse example.
Rafe
@rjmk
Sep 08 2016 12:08
Could you try the x => Handlebars.parse(x) thing? I think the issue is that Handlebars.parse has a length > 1 so the ifElse is curried to a length > 1. That's why neither the predicate nor Handlebars.parse are called
(Maybe)
Kurt Milam
@kurtmilam
Sep 08 2016 12:41
@rjmk You're right - to test, I just did ifElse(...)(valM, valM), and things started working correctly
So Handlebars.parse has length=2
First time I've run into that, so thanks for helping me walk through it. Will keep that issue in mind in case I run into similar behavior working with a method or function from a third party library.
Rafe
@rjmk
Sep 08 2016 12:45
@kurtmilam Glad it's fixed! Function arity is the big bugbear of FP libraries in JS
Syaiful Bahri
@syaiful6
Sep 08 2016 12:49
@kwijibo If you want to know the value inside reader you can call it with ask method on the reader.
Kurt Milam
@kurtmilam
Sep 08 2016 12:49
@rjmk Me too! This particular method isn't well documented. I took a look in the source, and it accepts an optional options argument. replacing Handlebars.parse with flip(Handlebars.parse)({})makes it work without having to pass a second 'dummy' value into the ifElse.
Syaiful Bahri
@syaiful6
Sep 08 2016 12:58
@kwijibo ah, my bad. for me each module can have it's own reader,i dont know if i doing it right or not, but in most practice i just use it where i need it. i built ecommerce website and i use Reader monad for currency calculation, and for payment gateway processing.
Keith Alexander
@kwijibo
Sep 08 2016 13:21
cheers @syaiful6
Barry G
@bgits
Sep 08 2016 14:55
what is the purpose of R.add considering it does not handle floating point precision errors?
LeonineKing1199
@LeonineKing1199
Sep 08 2016 15:03
Is that a serious question about arbitrary precision arithmetic in JS?
Actually, you might love Julia.
Julia was coded from the ground-up with that kind of support in mind and has a native bignum library
Actually, this is a really cool read about how this guy came up with a method for arbitrary floating point precision. It's a good introduction to floating point representations.
Barry G
@bgits
Sep 08 2016 15:06
No, it's a serious question about why Ramda needs an add function when it does the same thing as the + operator?
LeonineKing1199
@LeonineKing1199
Sep 08 2016 15:07
Oh, it's a convenience wrapper for use in compositions
For example, it's very convenient to just type R.reduce(R.add, 0, myNums)
But you still read about the arbitrary precision stuff. Really neat.
Barry G
@bgits
Sep 08 2016 15:09
i see
LeonineKing1199
@LeonineKing1199
Sep 08 2016 15:10
Math is hard, man. Matrix determinants. Matrix. Determinants.
Barry G
@bgits
Sep 08 2016 15:18
I'm looking at transduce in trying to understand it, I'm looking at the example in the repl, but can not understand why call R.transduce(transducer, R.flip(R.append), [], numbers); instead of transducer(numbers) ?
LeonineKing1199
@LeonineKing1199
Sep 08 2016 15:26
I think that case, the transduction does a single pass whereas the direct composition does two passes
Barry G
@bgits
Sep 08 2016 15:27
is there a better example? I still don't understand transduce.
LeonineKing1199
@LeonineKing1199
Sep 08 2016 15:28
Ooh, there's a really awesome article written by a guy from Twitter
Let me find it
Couldn't find it. But this one is a good read
It's funny, I learned transducers too and I've used them 0 times out in the wild :P
Brad Compton (he/him)
@Bradcomp
Sep 08 2016 15:31
That one's good! This one is in the wiki
Barry G
@bgits
Sep 08 2016 15:32
hmm, I read that about a month ago, and did not understand. I sort of understand the mechanics, I'm just having a hard time understanding the value in using it.
LeonineKing1199
@LeonineKing1199
Sep 08 2016 15:33
A lot of the Ramda functions can be made part of the transformation stack in the transduction
If you ever find yourself using a bunch of those over a list, a transducer would be appropriate
Kurt Milam
@kurtmilam
Sep 08 2016 15:56
Any tips on making this function point free? Or maybe there's an entirely better way to get where I want to go with it?
const getHBExpressionInfo = expr => map(
  x => [head(x), last(x)(expr)]
  , [
    ['type', prop('type')]
    , ['expression', getHBExpressionOriginal]
    , ['isHelper', isHBExpressionHelper]
  ]
)
I hope I'm not wearing out my welcome. I'm enjoying converting some existing code to point free style with Ramda. It's going smoothly, for the most part, but I do run into some roadblocks now and then.
Drew
@dtipson
Sep 08 2016 16:30
Would it be fair to say that Reader is a functional type that essentially captures the closure pattern in a principled way?
Ryan Zeigler
@rzeigler
Sep 08 2016 17:12
reader’s do not form closures at all
the are fully explicit unlike closures
Ryan Zeigler
@rzeigler
Sep 08 2016 17:30
well, i suppose they could, but in general the point is to receive all dependencies via the environment param
Drew
@dtipson
Sep 08 2016 17:31
that's what I mean though: it's purpose is to replace the closure pattern with a functional one, right?
instead of capturing the environment in a closure so that you can reference it, you get a way to reference it that externalizes it to the edge
I mean, something like .chain(x=>Reader.ask.map(y=>x+y)) itself a closure, no?
Keith Alexander
@kwijibo
Sep 08 2016 17:52
@dtipson I've been thinking of it more from the POV of there are a bunch of different ways of providing a block of code with its dependencies
  • Classes
  • parent closure scope
  • import/require
  • function arguments (w & w/o partial application)
  • Reader monad
The Reader monad's advantage, I think, is that it allows composability while allowing the caller to provide the dependencies
If you rely on scope, then the dependencies are hardwired so you can't provide different dependencies for tests etc (unless you use something hacky like rewire)
Keith Alexander
@kwijibo
Sep 08 2016 17:59
If you provide it as an argument to a function or class's constructor, then you either need to know it up front, or you lose composability
Ryan Zeigler
@rzeigler
Sep 08 2016 18:09
i strongly dislike the use of classes because you must construct a thunk or use bind in order to use it as a function
this leads to confusion and strange bugs
although thats more a function of the language, C# has no such issue with delegates as far as I know
Keith Alexander
@kwijibo
Sep 08 2016 18:30
(The Reader monad lets you keep composing with .map and .chain, and lets you provide the dependencies at the last moment with .run)
Barry G
@bgits
Sep 08 2016 18:43
How can I flatten an object using Ramda? In this example nested will be removed and the two objects will be added to the top level: https://dpaste.de/6dV1
Brad Compton (he/him)
@Bradcomp
Sep 08 2016 19:11
@ram-bot
converge(merge, [dissoc('nested'), prop('nested')])({id: 1, build: 12, nested: {stuff: 'things', more: true}})
ram-bot
@ram-bot
Sep 08 2016 19:11
{ id: 1, build: 12, stuff: 'things', more: true }
Brad Compton (he/him)
@Bradcomp
Sep 08 2016 19:12
@bgvianyc I like this method because you can then abstract away 'nested' to lift any property to the top level
Barry G
@bgits
Sep 08 2016 19:15
@Bradcomp how about in the case where nested has a field called id so there would be a conflict with the parent id, how would you cover that case?
Brad Compton (he/him)
@Bradcomp
Sep 08 2016 19:17
How would you want it covered? In this case the inner will overwrite the outer. if you use flip(merge) instead of merge the outer will take precedent. You can also use mergeWith or mergeWithKey for more granular behavior.
Barry G
@bgits
Sep 08 2016 19:18
how about this rule: if there is a conflict, concat the field name with "nested"?
Brad Compton (he/him)
@Bradcomp
Sep 08 2016 19:19
use mergeWith(concat) as your converging function
Oh, wait
nevermind
I misunderstood
Barry G
@bgits
Sep 08 2016 19:21
ie: nestedId would be the field name
Brad Compton (he/him)
@Bradcomp
Sep 08 2016 19:21
You would need to map over the keys of the nested object
There are ways of doing that, but it's more involved than a simple one liner
LeonineKing1199
@LeonineKing1199
Sep 08 2016 19:33
Brad Compton (he/him)
@Bradcomp
Sep 08 2016 19:34
Wow! Look at that
Barry G
@bgits
Sep 08 2016 19:38
@LeonineKing1199 is that map the ramda map or native?
Brad Compton (he/him)
@Bradcomp
Sep 08 2016 19:44
Looks like R.map
LeonineKing1199
@LeonineKing1199
Sep 08 2016 19:48

@LeonineKing1199 is that map the ramda map or native?

I'm assuming that example was written in a destructured way so map and chain are the Ramda functions

So there's ton of nice Object-specific functions out there too