These are chat archives for ramda/ramda

23rd
Mar 2016
Lewis
@6ewis
Mar 23 2016 00:38
@6ewis I see
@JAForbes thanks
Jonah
@jonahx
Mar 23 2016 03:10
@CrossEye, on the subject of pipe, what are your thoughts on monkeypatching array to get some sugar like:
  Array.prototype.pipe = function(...fns) { 
    return apply(pipe)(fns)(this[0]) 
  };

  var double = x => 2*x;
  var plusOne = x => x+1;
  [1].pipe(double, plusOne); //=> 3
Lewis
@6ewis
Mar 23 2016 03:26
@jonahx what does apply(pipe)(fns)(this[0]) do? I don't follow
Scott Sauyet
@CrossEye
Mar 23 2016 03:27
@jonahx: Well, I'm not much of a fan of monkey-patching the native/host functions, so that already would bother me. But even more importantly, I'm not a fan of such chained APIs. They privilege just a few well-known functions at the expense of others you might like to use.
R.apply
Jonah
@jonahx
Mar 23 2016 03:48
@CrossEye, yeah, it’s definitely against the grain of ramda’s awesome everything is composable philosophy. i just kind of liked it for the specific case of pipe, since the metaphor is a left to right pipeline, and the data at the end, in this case, breaks that metaphor.
Jonah
@jonahx
Mar 23 2016 03:55
A more practical question: How could rewrite the following function in a way that would allow me to remove the x completely, ie, as a pure composition of ramda function:
var mergeWithRandom = x => () => merge(x, {a:Math.random()})
Keith Alexander
@kwijibo
Mar 23 2016 09:52
R.converge(R.merge, [R.identity, R.pipe(random, R.objOf('a'))])
@ram-bot
const mergeWithRandom = R.converge(R.merge, [R.identity, R.pipe(Math.random, R.objOf('a'))])
mergeWithRandom({b:42})
Keith Alexander
@kwijibo
Mar 23 2016 09:58
@jonahx ^
@ram-bot R.add(1,4)
ram-bot
@ram-bot
Mar 23 2016 09:58
5
Keith Alexander
@kwijibo
Mar 23 2016 09:59
@ram-bot R.converge(R.merge, [R.identity, R.pipe(Math.random, R.objOf('a'))])({x: 99})
ram-bot
@ram-bot
Mar 23 2016 09:59
{ x: 99, a: 0.7962673644069582 }
Keith Alexander
@kwijibo
Mar 23 2016 10:00
I'm pretty sure whenever I've used converge one of the convergers is identity
Aldwin Vlasblom
@Avaq
Mar 23 2016 10:02
S.S
@ram-bot S.S
ram-bot
@ram-bot
Mar 23 2016 10:03
undefined
Aldwin Vlasblom
@Avaq
Mar 23 2016 10:03
Hm
Keith Alexander
@kwijibo
Mar 23 2016 10:04
@Avaq is S.S like converge with identity?
@kwijibo Yeah: S.S(R.merge, R.pipe(Math.random, R.objOf('a')))({x: 93}) :)
Keith Alexander
@kwijibo
Mar 23 2016 10:37
@Avaq is there any rhyme or reason as to which combinators are named after which letters?
Scott Sauyet
@CrossEye
Mar 23 2016 10:37
converge was written without thinking particularly about the S-like cases, and I use it in other ways as well, but such cases are also pretty common. We might well want to add S to Ramda, even if under a different name.
Keith Alexander
@kwijibo
Mar 23 2016 10:37
(how do you remember which is which, and what order the arguments are? any tips?)
Scott Sauyet
@CrossEye
Mar 23 2016 10:39
I don't think there is any scheme for them, only an old tradition.
How to remember? Keep a list handy until they're familiar. :smile:
Aldwin Vlasblom
@Avaq
Mar 23 2016 10:40
@kwijibo I think many of the letters come from birds names and come from the To Mock a Mockingbird book. I've documented several common combinators in this gist. Most of the ones I've documented have proper names that start with the letter assigned to the combinator, which is how I remember them. S is for substitution (which, coincidentally, @CrossEye, might be a good way to name it in Ramda?)
Keith Alexander
@kwijibo
Mar 23 2016 10:42
@Avaq what is being substituted?
Scott Sauyet
@CrossEye
Mar 23 2016 10:43
I'm pretty sure the bird names came later. Smullyan simply used birds with the right initial to give a different mnemonic device than plain letters.
Aldwin Vlasblom
@Avaq
Mar 23 2016 10:43
@CrossEye Ah, I see. :)
Scott Sauyet
@CrossEye
Mar 23 2016 10:45
I agree, if we do include it, substitute_ or _substitution would be appropriate.
Keith Alexander
@kwijibo
Mar 23 2016 10:45
(as to names, I was thinking you would have left and right variants , maybe like convergeLeft = (f, left) => R.converge(f, [left, identity]) )
Aldwin Vlasblom
@Avaq
Mar 23 2016 10:45
@kwijibo S comes from the SKI system as far as I'm aware. The S combinator is described as a substitution operator.
I'm not sure why exactly the math people named it substitution. :)
Keith Alexander
@kwijibo
Mar 23 2016 10:46
:)
Aldwin Vlasblom
@Avaq
Mar 23 2016 11:18
@kwijibo For some combinators I associate the shape of the letter with the flow of the code:
  • I: A single pipe. Value goes in, value comes out.
  • K: A single pipe, like I, with a second pipe bouncing off it, representing the second value being discarded
  • W: Double V, for value, representing duplication of a value
  • C: :arrow_right_hook: flip
  • B: The two bumps represent transformations as the value flows through the pipe
  • S: Like weaving, difficult to describe but makes sense in my head :P
Denis Stoyanov
@xgrommx
Mar 23 2016 11:20
@Avaq hm C is reverse(flip)?
Keith Alexander
@kwijibo
Mar 23 2016 11:38
@Avaq nice effort :)
Scott Sauyet
@CrossEye
Mar 23 2016 11:44
@Avaq: very nice. I'm going to remember those. I just found this bit of history: http://www.johndcook.com/blog/2014/02/06/schonfinkel-combinators/
Keith Alexander
@kwijibo
Mar 23 2016 11:54
naming every algorithm with single characters reminds me of K (APL)
Denis Stoyanov
@xgrommx
Mar 23 2016 11:56
Also I know about this one http://jwodder.freeshell.org/lambda.html
Sebastian Luberriaga
@Lube
Mar 23 2016 12:26
Hi guys, general question here, anyone knows some practical use for applicative functors?
Aldwin Vlasblom
@Avaq
Mar 23 2016 12:45

@Lube On Futures, which are applicative functors which represent an asynchronous value, you can use the interface to combine the values of two Futures run in parallel:

//The following line creates a Future of [a, b], but taskA and taskB are running in sequence
taskA().chain(a => taskB().map(b => [a, b]))

//The next line creates the same result, but it runs the two Futures in parallel
Future.of(a => b => [a, b]).ap(taskA()).ap(taskB())

I use this trick quite a lot. Or say we have two Maybe's of which we want to combine the results somehow:

const a = S.parseJson('1');
const b = S.parseJson('2');

Maybe.of(R.add).ap(a).ap(b)
//or
a.map(R.add).ap(b)
//> Just(3)
Sebastian Luberriaga
@Lube
Mar 23 2016 12:47
The first example is great, since you touched the issue, the only difference between Future and promises, is that a Future can delay its execution?
Aldwin Vlasblom
@Avaq
Mar 23 2016 12:53

@Lube Good question. I intent to write an article explaining the differences in detail on the wiki of Fluture. Currently I've tried to highlight some of the differences throughout the readme, especially in the then branch.

I've once summarized the differences for someone some time ago. I'll paste my summary down below, but I will say I'll have to review it carefully (and remove the obvious bias ;)) before publishing it in the wiki:

  • Promises are eager. They execute even if you don't give them handlers, sometimes causing them to swallow returned values.
  • Promises are magical. They do different things based on the type of their contained value.
  • Promises are made up. They don't follow any standard API, even though they follow many of the same standard patterns.
  • Promises always cache. There is no way to re-execute the operation in the Promise.
  • Futures are lazy/pure. They only start doing stuff when they're told to, allowing us to extend the computation.
  • Futures are logical. They always do the same thing when performing the same function.
  • Futures are lawful. They follow the standard Monadic API (as defined by Fantasy Land) and are therefore easy to abstract over.
  • Futures can cache. They don't cache by default, but it's quite easy to add it for all your caching needs.
Keith Alexander
@kwijibo
Mar 23 2016 12:56
Seems a bit harsh to say that https://promisesaplus.com/ isn't a standard?
Aldwin Vlasblom
@Avaq
Mar 23 2016 12:59
@kwijibo Yeah, I know. It's badly worded. What I mean to highlight there is that the Promise standard isn't as generalized as it could be. Promises are almost Monads, but with such a specialized API that any abstraction made has to be made specifically for it.
Sebastian Luberriaga
@Lube
Mar 23 2016 13:00
Thanks for the insight Avaq, I've becomed so comfortable to chain and compose promises in the last few months, and I can see some of these pitfall you mention, and the theoretical framework that seems inconsistent between the implementations.
I will look into Futures, and how it can make my logic more consistent when working with async operations!
Scott Sauyet
@CrossEye
Mar 23 2016 13:01
In fact, the FantasyLand standard was a reaction to the resistance to an attempt to add more algebraic standardization to drafts of the Promises A+ standard.
(Wow that was a mouthful!)
Keith Alexander
@kwijibo
Mar 23 2016 13:02
that's interesting
Aldwin Vlasblom
@Avaq
Mar 23 2016 13:02
to*4 ;)
@CrossEye Cool, I had no idea!
Scott Sauyet
@CrossEye
Mar 23 2016 13:03
Some folks asked to add some algebraic standards into the Promises spec. It was late in the game, very controversial, and there was a great deal of animosity in the discussion. The very name "FantasyLand" was an appropriation of a put-down used by an opponent.
Sebastian Luberriaga
@Lube
Mar 23 2016 13:04
I found that hilarious, lol
Scott Sauyet
@CrossEye
Mar 23 2016 13:05
Very long thread: promises-aplus/promises-spec#94
Keith Alexander
@kwijibo
Mar 23 2016 13:06
"Monads are no match for a good blaster at your side kid" http://existentialcomics.com/comic/103
Scott Sauyet
@CrossEye
Mar 23 2016 13:07
nice
Aldwin Vlasblom
@Avaq
Mar 23 2016 13:08
@CrossEye By Paulmillr o.o - I didn't know he was into this stuff. I used to use his Chaplin framework and Brunch a long time ago!
Keith Alexander
@kwijibo
Mar 23 2016 13:15
actually has promisesA or A+ or whatever been surpassed by es2015 incorporating promises into javascript?
Aldwin Vlasblom
@Avaq
Mar 23 2016 13:27
@CrossEye Still reading the thread. I find it shocking how people are completely missing the point puffnfresh is trying to make over and over again. Very interesting. He's saying exactly what I tried to say with "They don't follow any standard API" though, wording it differently every time and being misunderstood at every attempt of wording it.
Keith Alexander
@kwijibo
Mar 23 2016 13:28
as I started reading, I thought "that got ugly fast"
Aldwin Vlasblom
@Avaq
Mar 23 2016 13:30
Haha, I know. Developers are passionate people. (If you wanted a euphemism ;))
Keith Alexander
@kwijibo
Mar 23 2016 13:41
@Avaq on the subject of Future's; is there a difference between a Future and a Stream?
const clicks = el => Future((_,resolve)=>{  el.onclick(resolve) })
seems to do similar things to many stream libraries I've seen
Aldwin Vlasblom
@Avaq
Mar 23 2016 13:45
Except that resolve would be undefined if you haven't forked the Future yet. But it's interesting food for thought. They are certainly very similar.
Keith Alexander
@kwijibo
Mar 23 2016 13:46
@Avaq it's not undefined is it? The rejectResolve function isn't called until fork is called
so the clickHandler isn't registered until fork is called
Aldwin Vlasblom
@Avaq
Mar 23 2016 13:50

The rejectResolve function literally is the fork function (except that Fluture decorates it with type-checking):

const Future = fork => ({fork, map: ..., chain: ...})

So yeah, you're right. :)

Lewis
@6ewis
Mar 23 2016 13:52
@CrossEye Are you on irc
Aldwin Vlasblom
@Avaq
Mar 23 2016 13:53

resolve would be undefined if you haven't forked the Future yet

I was thinking about a different piece of code I wrote when exploring the differences between Promise and Future. Excuse the mistake. :)

Keith Alexander
@kwijibo
Mar 23 2016 14:04
@Avaq no worries
Aldwin Vlasblom
@Avaq
Mar 23 2016 14:07
@kwijibo Maybe I should look at greater extend to the differences between Future and Stream. Perhaps the only difference lies in how we (or I) view them. Maybe we should've been using Streams all along as they're the only logical conclusion to working with asynchronous values. I find it all quite mind-blowing.
Lewis
@6ewis
Mar 23 2016 14:08
I didn't even know about Future
javascript move so fast that's insane
Keith Alexander
@kwijibo
Mar 23 2016 14:08
I think Future, as a term, is from another language(s?), pre-Promises
Lewis
@6ewis
Mar 23 2016 14:09
so what's the difference between future and promises
Aldwin Vlasblom
@Avaq
Mar 23 2016 14:09
@6ewis Scroll up a bit, we've been talking about exactly that for an hour. :)
Keith Alexander
@kwijibo
Mar 23 2016 14:10
:)
@Avaq I like your super-minimalist Future definition there; could you flesh out the chain definition on it?
Aldwin Vlasblom
@Avaq
Mar 23 2016 14:14
@6ewis I think JavaScript appears to move so fast because JavaScript is a common territory on which people from all different communities perform work in the way they know from their background. JavaScript is multi-paradigm enough to handle all of the different ways of working, and so everywhere you look people work in surprisingly different ways. Every time you turn 1.5 degrees you'll find yourself looking at a completely new way of working and you'll think to yourself: "Wow! JavaScript has changed!" while in reality you're just looking at a different community.
And once you've finally turned full-circle, the place you started has changed so much you can hardly recognize it. I really enjoy the JavaScript ecosystem. :)
Aldwin Vlasblom
@Avaq
Mar 23 2016 14:20

could you flesh out the chain definition on it -- @kwijibo

I really should, shouldn't I? It's kind of lackluster as far as documentation goes. I'll see to it. :)

Lewis
@6ewis
Mar 23 2016 14:21
@Avaq I don't know about that. I have a different take - Not only is Javascript easy to get into as opposed to many other languages, but it's the main language used by browsers. I believe it's ubiquitous for that reason, consequentially people come up with new libraries at a faster rate than any other language. If we're able to see it move that fast it's only because we stand on the shoulder of giants; we build on top of other people and innovate in the process. The inevitable downside is that there are a lot of code that is not simple to understand because people put whatever they can put out there.
Keith Alexander
@kwijibo
Mar 23 2016 14:26
@Avaq I think I figured it out:
const Future = fork => {
 fork,
 map: f => Future((rej,res)=> fork(rej, compose(res,f))),
 chain: f => Future((rej, res)=> {
  fork(rej, future=> future.map(f).map(res))
 })
}
Aldwin Vlasblom
@Avaq
Mar 23 2016 14:32
Your chain implementation is slightly off. It assumes that you're dealing with a Future<Future> and that f won't return a Future.
chain calls f on the inner, just like map would, but instead expects f to return the new container, rather than the new inner.
Keith Alexander
@kwijibo
Mar 23 2016 14:36
yeah it's totally wrong actually
Aldwin Vlasblom
@Avaq
Mar 23 2016 14:39

Take the Identity Monad (the simplest of Monads):

const Id = x => ({x, map: f => Id(f(x)), chain: f => f(x)})

Here it should be quite obvious what the difference is. chain returns Id because the f its given does.

Drew
@dtipson
Mar 23 2016 14:40
my favorite thing about that Promises thread and FL is that the very first commit to Fantasy-Land was made the day after that epithet was hurled
Aldwin Vlasblom
@Avaq
Mar 23 2016 14:40
Haha :D
Risto Stevcev
@Risto-Stevcev
Mar 23 2016 14:42
Yeah, JS has so much library churn that if I think of something, it probably already exists and there are 3-10 popular libraries for it
Aldwin Vlasblom
@Avaq
Mar 23 2016 14:43
@kwijibo With Future's we can't just call f on the value, because we don't have the value yet. Instead we must return a new Future which forks the Future returned by f into its own branches:
const Future = fork => ({fork, chain: f => Future((rej, res) => fork(rej, x => f(x).fork(rej, res)))})
Denis Stoyanov
@xgrommx
Mar 23 2016 14:45
why not fantasy-future https://github.com/Avaq/Fluture ?
Aldwin Vlasblom
@Avaq
Mar 23 2016 14:45
@xgrommx https://github.com/Avaq/Fluture#the-name - Fluture means butterfly, which might one day become an appropriate logo. :)
Risto Stevcev
@Risto-Stevcev
Mar 23 2016 14:47
Turning dreams into reality
lol
Denis Stoyanov
@xgrommx
Mar 23 2016 14:49
@Avaq you want smth general which to aggregate fantasy-future and task together?
Aldwin Vlasblom
@Avaq
Mar 23 2016 14:50
@xgrommx Oh, I thought you were inquiring about the name. You were talking about https://github.com/jsanchesleao/fantasy-future! I didn't know about this project. :o
Keith Alexander
@kwijibo
Mar 23 2016 14:52
@Avaq nice - clear when I see it - I find it easy to get muddled up in nested continuations :)
Lewis
@6ewis
Mar 23 2016 14:53
Are you guys all coming from haskell
sheesh
nomad this nomad that
Aldwin Vlasblom
@Avaq
Mar 23 2016 14:54
I've recently started learning Haskell, but I learned all the jargon from Ramda and Fantasy Land. :)
Keith Alexander
@kwijibo
Mar 23 2016 14:54
prior to javascript, I think my only FP experience was XSLT
kwijibo @kwijibo wonders if there is a nomad monad
Denis Stoyanov
@xgrommx
Mar 23 2016 14:55
Heh! I love this blog and also I like what this guy doing https://medium.com/@drboolean/free-er-monads-in-js-f5a59e7abc82#.9gci4yak6
Keith Alexander
@kwijibo
Mar 23 2016 14:56
@xgrommx also, did you see this? https://github.com/DrBoolean/freeky
Lewis
@6ewis
Mar 23 2016 14:56
@Avaq I see. What's fantasy land for
Denis Stoyanov
@xgrommx
Mar 23 2016 14:57
@kwijibo yeah! this blog about it
Aldwin Vlasblom
@Avaq
Mar 23 2016 14:58
@xgrommx My primary motivation for creating Fluture was to add the best error messages I can get into Future's. No existing implementations of Future tell you what you did wrong when something breaks. Instead you get f(...).fork is not a function - at <anonymous> at <anonymous> at <anonymous> .... I've been using Futures a lot and I got tired of that. :)
Sebastian Luberriaga
@Lube
Mar 23 2016 14:59
ramda-fantasy futures do this too?
Aldwin Vlasblom
@Avaq
Mar 23 2016 14:59
Yeah
@6ewis Fantasy Land is a specification, and collections of implementations, for algebraic data-types in JavaScript. It helps everyone who wants to work with Monads in JavaScript interoperate.
Lewis
@6ewis
Mar 23 2016 15:07
@Avaq you understood monad by reading fantasy land? Jesus
Aldwin Vlasblom
@Avaq
Mar 23 2016 15:08
I understood monad by reading the source code of many monads. :P
With some help from DrBooleans Monad a Day series.
Lewis
@6ewis
Mar 23 2016 15:12
I see
Risto Stevcev
@Risto-Stevcev
Mar 23 2016 15:12
I don't really see why people get so confused about monads. How is it any different from learning other categories?
If you know how the type system works, you know how all of that stuff works....
Jonah
@jonahx
Mar 23 2016 15:18
@kwijibo thanks! perfect. Also, I guess ramda does not have the S combinator like @Avaq used?
Keith Alexander
@kwijibo
Mar 23 2016 15:19
I'm not sure really knowing how the type system works is a given :)
monads can be hard to understand probably because they are abstract; specific monads, like Maybe don't seem particularly problematic
LeonineKing1199
@LeonineKing1199
Mar 23 2016 15:22
Can someone explain to a curmudgeon C++ programmer what a Monad is? To me, Ramda seems like a super awesome utils library and I'd like to be more well-versed in functional programming.
Keith Alexander
@kwijibo
Mar 23 2016 15:22
also the continuation passing thing; even Promises tend to get people pretty confused at first
Drew
@dtipson
Mar 23 2016 15:25
I think as a newbie coming into trying to understand Fantasy Land, you sort of have to first understand the basic idea of creating the weakest possible set of restrictions for types. Like with the Functor laws: if you just read the laws without every having used .map on anything but an Array, as many people have, you have no idea how powerful map is or what the core idea is, because the laws don't tell you anything other than what ALL Functors must do
But that's exactly their power: a set of things you can rely on so that all the fun specific behaviors of different Functors conform
Keith Alexander
@kwijibo
Mar 23 2016 15:26
@LeonineKing1199 I can try ... Monad is a particular interface that types can implement so that they can work together
LeonineKing1199
@LeonineKing1199
Mar 23 2016 15:26
Huh...
I see. Other languages do that too. Okay, that's not so bad
Drew
@dtipson
Mar 23 2016 15:27
You need to see a couple of different Monads to see both how they differ as well as how they are all the same
Keith Alexander
@kwijibo
Mar 23 2016 15:27
common specific types of Monad are Maybe, Either, Future (also called Task)
6ewis @6ewis is considering learning haskell to master those concepts
Drew
@dtipson
Mar 23 2016 15:34
Monads are just Functors that also implement a) a functional way to elevate a value (where "value" can also be a function) into the Type and b) a method that expects a function which will get the inner value of the Monad and return a new Monad of the same type
Lewis
@6ewis
Mar 23 2016 15:34
@dtipson are you coming from haskell
LeonineKing1199
@LeonineKing1199
Mar 23 2016 15:35
Wait, what does "elevate a value" mean?
Stefano Vozza
@svozza
Mar 23 2016 15:35
A monad is just a monoid in the category of endofunctors. Simple.
Drew
@dtipson
Mar 23 2016 15:35
Array.of(8) -> [8]
LeonineKing1199
@LeonineKing1199
Mar 23 2016 15:35
Oh, it takes a value and returns a type with that value. Ha!
Drew
@dtipson
Mar 23 2016 15:36
yeah, just puts the value into the type. It's not always so easy to do that. But that's the basic idea (and the particular complications per type are often what makes them different types)
LeonineKing1199
@LeonineKing1199
Mar 23 2016 15:36
This message was deleted
Keith Alexander
@kwijibo
Mar 23 2016 15:37
It's sort of strange that Monads have this mystique about them, when they're just Functors with a couple extra methods, and Functors are the main thing to understand
Drew
@dtipson
Mar 23 2016 15:37
M.of(5).chain( x => M.of(x+1) ) = M[6]
Aldwin Vlasblom
@Avaq
Mar 23 2016 15:38
Monads only have a mystique about them to people who also don't know what Functors are.
Drew
@dtipson
Mar 23 2016 15:38
M.map = function(f){ this.chain( x = M.of( f(x) ) ); }
Aldwin Vlasblom
@Avaq
Mar 23 2016 15:38
Once you understand the idea of a Functor, you can explain Monads by explaining the difference between map and flatMap/chain/bind
Denis Stoyanov
@xgrommx
Mar 23 2016 15:39
If we have chain and of we can make everything :smile: maybe
Drew
@dtipson
Mar 23 2016 15:40
I walked through making native Arrays Monadic, without actually explaining what Monads are recently: https://medium.com/@dtipson/hey-let-s-make-a-monad-e276802fdb0c#.3h3xq6sh8
Risto Stevcev
@Risto-Stevcev
Mar 23 2016 15:40
@LeonineKing1199 What @dtipson said. Depending on the language, they call the method that elevates the value pure or return, and the the method that unboxes the monad and returns a new monad of that type chain or bind. So pure is just m a and chain is a -> m b
Drew
@dtipson
Mar 23 2016 15:40
which I think helped me finally get it, unless everything in that article is wrong
Risto Stevcev
@Risto-Stevcev
Mar 23 2016 15:40
@kwijibo I think maybe the mystique comes from the IO monad
If that didn't exist and it was just Maybe chaining, Either, etc... it might just be viewed as everything else
Drew
@dtipson
Mar 23 2016 15:41
IO can make your head hurt
crazzzy
Drew
@dtipson
Mar 23 2016 15:42
I'm strongly in favor of, if they're going to add .flatten and .flatMap to Arrays, that they also implement .ap
Risto Stevcev
@Risto-Stevcev
Mar 23 2016 15:43
And the IO monad was really developed because Haskell is lazy
Drew
@dtipson
Mar 23 2016 15:43
Might be too late to get them to call .flatMap .chain
Risto Stevcev
@Risto-Stevcev
Mar 23 2016 15:44
And Haskell's laziness makes it purely functional
Drew
@dtipson
Mar 23 2016 15:45
.ap, which Monads also all have "for free" (i.e. once you have defined point/.of and .chain properly, you know for sure how to trivialy define .ap) is incredibly useful and expressive
Risto Stevcev
@Risto-Stevcev
Mar 23 2016 15:46
In a strictly evaluated language, you don't really need an IO monad, unless you want laziness. But we have Future to do that
And LazyEither ;)
I think also that a lot of people get confused because they think they need to know category theory to understand this stuff
Jonah
@jonahx
Mar 23 2016 15:48
Practical question on the subject being discussed. What libraries (or perhaps set of ramda functions / techniques) are you all using when implementing these idea to contain business logic. For example, I was happy with http://dry-rb.org/gems/dry-transaction/ in ruby. Not sure what the equivalent is in JS-land, or if you need to roll your own out of ramda methods or similar libraries….
Risto Stevcev
@Risto-Stevcev
Mar 23 2016 15:51

This would be similar to a monad:

A business transaction is a series of operations where each can fail and stop processing.
When implementing chain, the computation will effectively "halt" with a "failed" value. This is true with Maybe, Either, Future, LazyEither, etc...
A business transaction resolves its dependencies using an external container object and it doesn’t know any details about the individual operation objects except their identifiers.
You can perform kliesli composition with a chain of the same monads, without worrying how it works under the hood
A business transaction can describe its steps on an abstract level without being coupled to any details about how individual operations work.
See previous
A business transaction doesn’t have any state.
Monads don't normally have state (except for the State monad) since it simulates the notion of a computation with chain
Each operation shouldn’t accumulate state, instead it should receive an input and return an output without causing any side-effects.
See above
The only interface of a an operation is #call(input).
Each operation provides a meaningful functionality and can be reused.
Yeah
Errors in any operation can be easily caught and handled as part of the normal application flow.
Yeah

Drew
@dtipson
Mar 23 2016 15:51
yeah, a lot of what IO does is eclipsed by other more complex Monads which, ironically, people already use and understand without realizing it
(i.e. Promises)
LeonineKing1199
@LeonineKing1199
Mar 23 2016 15:53

"What’s a Monad? Well, it’s just a “pointed Functor that can flatten.” Simple enough, no need to elaborate: let’s see if Array qualifies."

What does it mean in a general sense to "flatten"? Like, if we had a plain ol' data type in something like C, would it being taking all the members (even nested ones) and "flattening" them into an array of key-value pairs or just values?

Jonah
@jonahx
Mar 23 2016 15:53
@Risto-Stevcev yes, the whole thing is monads in disguise. my question was practically what are you using to do it? that library is essentially a very nice wrapper / DSL for expressing things in the language you want to use when thinking about business transactions. so, was curious if JS had something like it.
LeonineKing1199
@LeonineKing1199
Mar 23 2016 15:53
Well, I guess JS has POJOs...
Drew
@dtipson
Mar 23 2016 15:54
[[3,4],[1,2]] -> [3,4,1,2]
(though I get to that later)
LeonineKing1199
@LeonineKing1199
Mar 23 2016 15:54
But what about more complex types and representations of data?
Or does that not exist in FP?
Risto Stevcev
@Risto-Stevcev
Mar 23 2016 15:56
@dtipson Yeah definitely. I've heard someone describe Promises like a continuation monad.
Drew
@dtipson
Mar 23 2016 15:56
not sure what you mean. Arrays are more complex in the sense that they hold multiple values: many Monads just hold "one." So flattening is just going from M(M[7]) to M[7]
Risto Stevcev
@Risto-Stevcev
Mar 23 2016 15:56
And really, everyone already knows how the type system works more or less. It's not unlike interfaces in languages like Java, and types like Maybe implement things like the Monad interface, etc
Drew
@dtipson
Mar 23 2016 15:57
native Javascript Arrays are somehow terrible things though, which is why most FP implementations create other structures like Lists, Immutable Lists, etc.
Jonah
@jonahx
Mar 23 2016 15:57

@kwijibo looks like i spoke too soon before :) there’s a small but important difference in the code sample you gave me eariler. hoping you can help sort it out, as the difference is what i’m stuck on:

var mergeWithRandom = R.converge(R.merge, [R.identity, R.pipe(Math.random, R.objOf('a'))])
mergeWithRandom({b:42}) // returns final answer immeidately

var mergeWithRandom = x => () => merge(x, {a:Math.random()})
var x = mergeWithRandom({b:42}); // returns a generator
x(); // now returns final answer

So I want to write the generator version but in a point-free style.

Risto Stevcev
@Risto-Stevcev
Mar 23 2016 15:58
The main difference between fp and something like Java interfaces is that fp makes sure to decouple data structures from algorithms
Drew
@dtipson
Mar 23 2016 15:58
This (from this morning) is a really good summation of why Promises are wacky when compared to Futures/Tasks: https://gitter.im/ramda/ramda?at=56f291adba45ef634e8db478
Risto Stevcev
@Risto-Stevcev
Mar 23 2016 15:58
Object oriented programming, on the other hand, will couple the two. Which actually makes debugging large applications much more of a pain in the *ss
LeonineKing1199
@LeonineKing1199
Mar 23 2016 15:59
Flattening is going from M(M(7)) to M(7)... Now that sounds a little more generic...
Drew
@dtipson
Mar 23 2016 16:00
But they're still Monads, and hopefully have at least still helped a lot of people "get" some of the basics of Monads even if they're so weird
i.e. Promise.resolve(Promise.resolve(5)) -> Promise[5] instead of Promise[Promise[5]], because they "magically" flatten themselves
Risto Stevcev
@Risto-Stevcev
Mar 23 2016 16:04
@jonahx You can develop your own DSL by curring and composing generic functions if that's what you mean
In fp you have a set of generic functions that work on different types such as Functor, Monoid, Monad, etc. Then you can use a combination of partially applying these functions and composing them to create domain specific functions
Which can often save you a lot of time and bugs since you don't need to implement your whole business logic from scratch
Jonah
@jonahx
Mar 23 2016 16:06
@Risto-Stevcev yeah, my question was more like a library recommendation question / how are people that like ramda doing this? to be more specific, as an example: what do you use as your validation library? folktale data.validation? something from monet.js? something else?
Risto Stevcev
@Risto-Stevcev
Mar 23 2016 16:06
And you can save even more time with jsverify (the javascript version of quickcheck) to do your testing and negative testing
I use Either for validation
Jonah
@jonahx
Mar 23 2016 16:08
which implementation?
Risto Stevcev
@Risto-Stevcev
Mar 23 2016 16:08
If I chain Either types together in a pipeline with pipeK, if one of them fails that Left value will get propagated
Jonah
@jonahx
Mar 23 2016 16:08
ramda’s i assume?
Risto Stevcev
@Risto-Stevcev
Mar 23 2016 16:08
I wrote LazyEither while back, so I use that, along with sanctuary Either
ramda-fantasy is ok, but sanctuary is much more active in terms of development and more feature-rich, so I import ramda and sanctuary
Jonah
@jonahx
Mar 23 2016 16:10
cool, that’s the kind of info i was looking for
Risto Stevcev
@Risto-Stevcev
Mar 23 2016 16:10
You could also use Future which forks two different callbacks for failure and success, but I hate that syntax and find it really unnecessary since you could just have one callback with an encapsulated Either
which is what LazyEither does
Keith Alexander
@kwijibo
Mar 23 2016 16:11
@jonahx that's an interesting question: is it possible to compose a generator? they need that special * syntax don't they?
Jonah
@jonahx
Mar 23 2016 16:12
@kwijibo sorry, i didn’t mean a JS * generator, i just meant a function that you have to call again to generate your value. so more english defiition of “generaor"
if you look at the example in the snippet it should be clear
Risto Stevcev
@Risto-Stevcev
Mar 23 2016 16:12
I'm also planning on extending it soon when I have the time, to include a recover method that would have the same signature as chain but would instead not execute if it's a Right value but will if it's Left so that the user can try to recover the error
Keith Alexander
@kwijibo
Mar 23 2016 16:16

@Risto-Stevcev I was talking to @Avaq earlier about Future's working sort of like Streams, and I just realised an important different is Streams can complete
So a Stream construction might be like:

Stream((onError, onData, onComplete)=> { .... })

I wonder is there a way to align that with a LazyEither style interface?

@jonahx one way is simply to wrap it in fat arrows:
const myFunc x => () => myComposition(x)
Jonah
@jonahx
Mar 23 2016 16:21
@kwijibo so the general problem is (and this should be trivial to solve, but i can’t :( ) given a function that take a parameter and returns a value, change that into a function that take a parameter and returns another function which, when called, will return the value. and do it point-free.
Keith Alexander
@kwijibo
Mar 23 2016 16:22
@jonahx how about adding R.always to the end of your composition
Jonah
@jonahx
Mar 23 2016 16:25
@kwijibo ah, ofc. adding always to the end of the pipe did it. ty.
Drew
@dtipson
Mar 23 2016 16:26
So basically just want to return a thunk? Like, const thunkify = fn => (arg) => _ => fn(arg);
Risto Stevcev
@Risto-Stevcev
Mar 23 2016 16:26
@kwijibo Yeah, I think you could make a LazyEitherStream
that sounds like a good idea
Jonah
@jonahx
Mar 23 2016 16:28
@kwijibo @dtipson ugh, actually my actual, non-simplified problem is still not solved :( i’ll have to post a better sample problem later, need to leave for work. thanks for your help.
Keith Alexander
@kwijibo
Mar 23 2016 16:28
np
Drew
@dtipson
Mar 23 2016 16:28
or someFuncAsIO = arg => IO(_=>someFunc(arg)); someFuncAsIO(5).runUnsafeIO()
Risto Stevcev
@Risto-Stevcev
Mar 23 2016 16:29
It's basically the same deal, onError and onData would be joined into an Either type that would return either a Right value with the combined onData buffer chunks, or return the Left onError chunks and ignore any onData data that might still pass through.
Keith Alexander
@kwijibo
Mar 23 2016 16:31
but what about onComplete? I guess you have to abandon the symmetry of one type per event type, because it's not just error OR data, it's error OR data THEN complete
Risto Stevcev
@Risto-Stevcev
Mar 23 2016 16:32
that would be like value, except it would be lazier in a sense because it won't actually get called until the stream is complete
well, actually its kind of the same
value and fork will probably have some delay as it fetches whatever Future value it's getting.
I think it's a good idea
I think it works the same way that LazyEither does, it just needs to be written to work with the Stream interface
Risto Stevcev
@Risto-Stevcev
Mar 23 2016 17:02
it could also do other things that require boilerplate with regular streams
for example, pipeing one stream into another. With a LazyEitherStream, it could prevent pipeing an error stream, or provide an option to allow it, etc
Scott Sauyet
@CrossEye
Mar 23 2016 18:40
@jonahx: I generally use Folktale. It seems the most complete, battle-tested version available.
Jonah
@jonahx
Mar 23 2016 18:41
@CrossEye thanks for the recommendation!
Keith Alexander
@kwijibo
Mar 23 2016 18:54
I wonder if it's a bug that (ramda) Futures & (folktale) Tasks can be resolved multiple times
joneshf-work1
@joneshf-work1
Mar 23 2016 18:59
Is there something like (a | [a]) -> [a]?
So it stuffs it into an array if it's not there already
or do I need to turn to something like jquery
Keith Alexander
@kwijibo
Mar 23 2016 19:00
R.ifElse(R.is(Array), R.identity, R.of) ?
Jonah
@jonahx
Mar 23 2016 19:01
@kwijibo (or anyone else interested), a clearer statement of my problem from earlier is to rewrite rngN in a point free style:
var rngN = x => () => Math.floor(x*Math.random());
var rng10 = rngN(10);
times(rng10, 8)
// => [4, 0, 2, 9, 4, 0, 0, 6]
Keith Alexander
@kwijibo
Mar 23 2016 19:01
This message was deleted
ram-bot
@ram-bot
Mar 23 2016 19:01
[Function: f1]
Keith Alexander
@kwijibo
Mar 23 2016 19:02
@ram-bot R.of(42)
ram-bot
@ram-bot
Mar 23 2016 19:02
[ 42 ]
Keith Alexander
@kwijibo
Mar 23 2016 19:03
This message was deleted
ram-bot
@ram-bot
Mar 23 2016 19:03
[Function: f1]
Keith Alexander
@kwijibo
Mar 23 2016 19:04
@ram-bot R.chain(R.of, 42)
ram-bot
@ram-bot
Mar 23 2016 19:04
[ undefined ]
Keith Alexander
@kwijibo
Mar 23 2016 19:04
@ram-bot R.chain(R.of, R.of(42))
ram-bot
@ram-bot
Mar 23 2016 19:04
[ 42 ]
Keith Alexander
@kwijibo
Mar 23 2016 19:06
@ram-bot R.pipe(R.of, R.chain(R.identity))(42)
ram-bot
@ram-bot
Mar 23 2016 19:06
[ 42 ]
Keith Alexander
@kwijibo
Mar 23 2016 19:06
@ram-bot R.pipe(R.of, R.chain(R.identity))([42])
ram-bot
@ram-bot
Mar 23 2016 19:06
[ 42 ]
Keith Alexander
@kwijibo
Mar 23 2016 19:06
@ram-bot R.pipe(R.of, R.chain(R.identity))([[42]])
ram-bot
@ram-bot
Mar 23 2016 19:06
[ [ 42 ] ]
Risto Stevcev
@Risto-Stevcev
Mar 23 2016 19:07
@ram-bot R.when(R.pipe(R.is(Array), R.not), R.of)([1])
ram-bot
@ram-bot
Mar 23 2016 19:07
[ 1 ]
Keith Alexander
@kwijibo
Mar 23 2016 19:07
I think this has come up a few times before, and I've not 100% sure there isn't a ramda function that does it ..
Risto Stevcev
@Risto-Stevcev
Mar 23 2016 19:08
Yeah I also feel like there's a more shorthand form
It might be something like what you've been trying
joneshf-work1
@joneshf-work1
Mar 23 2016 19:12
eh, it's a kludgey thing to have to check, so jquery sounds right for the job
Was just curious if I was missing something
works fine
thanks for some ideas though :)
Denys Mikhalenko
@prontiol
Mar 23 2016 19:20
not related to ramda, but I usually use [].concat(x) for this purpose
joneshf-work1
@joneshf-work1
Mar 23 2016 19:22
That's probably a better solution.
Jonah
@jonahx
Mar 23 2016 19:22
yeah i like that
Denys Mikhalenko
@prontiol
Mar 23 2016 19:34
talking about ramda, you can try R.flatten([x])
but this might not work well for you, if you want to be able to keep nested arrays in x, as R.flatten will literally flatten them all
joneshf-work1
@joneshf-work1
Mar 23 2016 19:39
@ram-bot R.unnest([123])
ram-bot
@ram-bot
Mar 23 2016 19:39
[ 123 ]
Aldwin Vlasblom
@Avaq
Mar 23 2016 19:39
@ram-bot [R.unnest([1]), R.unnest([[1]])]
ram-bot
@ram-bot
Mar 23 2016 19:39
[ [ 1 ], [ 1 ] ]
Aldwin Vlasblom
@Avaq
Mar 23 2016 19:39
Hah
joneshf-work1
@joneshf-work1
Mar 23 2016 19:39
@ram-bot R.unnest([[1,2,3]])
ram-bot
@ram-bot
Mar 23 2016 19:39
[ 1, 2, 3 ]
joneshf-work1
@joneshf-work1
Mar 23 2016 19:40
The fact that the first one works doesn't feel right to me.
R.unnest
joneshf-work1
@joneshf-work1
Mar 23 2016 19:41
Shouldn't that throw something
Denis Stoyanov
@xgrommx
Mar 23 2016 19:43
R.ap
Denys Mikhalenko
@prontiol
Mar 23 2016 19:44
looks like R.unnest([x]) is the solution
Jonah
@jonahx
Mar 23 2016 19:59
This message was deleted
Sebastian Luberriaga
@Lube
Mar 23 2016 20:28
So which future library should we use, LazyEither, Folktale, Fluture, Fantasy-Future? Anyone has an article on that?
Jonah
@jonahx
Mar 23 2016 20:29
@Lube I just asked something similar earlier today, and @CrossEye suggested folktale. There was also a vote for LazyEither. Dont have experience myself, just passing that along.
Jonah
@jonahx
Mar 23 2016 20:43
If anyone with a few spare minutes can look at my SO post about writing a point free rng, I’d appreciate it
Denys Mikhalenko
@prontiol
Mar 23 2016 20:54
in your ramda example you're calling Math.random() directly, is this intended?
Sebastian Luberriaga
@Lube
Mar 23 2016 20:56

ram-bot R.times(R.compose(Math.floor, R.multiply(10), Math.random), 5);

@ram-bot R.times(R.compose(Math.floor, R.multiply(10), Math.random), 5);
Denys Mikhalenko
@prontiol
Mar 23 2016 20:57
omg
Jonah
@jonahx
Mar 23 2016 20:57
@prontiol, well, it’s intended as an attempt that doesn’t work :)
Denys Mikhalenko
@prontiol
Mar 23 2016 20:57
I mean, it will be called once, when you define the function
Jonah
@jonahx
Mar 23 2016 20:57
correct, which is the problem. but i can’t figure out how to fix it
Denys Mikhalenko
@prontiol
Mar 23 2016 20:58
Ah
Sebastian Luberriaga
@Lube
Mar 23 2016 20:58
@ram-bot R.times(R.compose(Math.floor, R.multiply(10), Math.random), 5);
ram-bot
@ram-bot
Mar 23 2016 20:58
[ 3, 8, 4, 6, 9 ]
Jonah
@jonahx
Mar 23 2016 20:58
@Lube, but now turn that into a statement rngN = ..stuff… where rngN works as in the original
Sebastian Luberriaga
@Lube
Mar 23 2016 21:00
@ram-bot R.times(R.compose(Math.floor, R.multiply(10), Math.random))(10);
ram-bot
@ram-bot
Mar 23 2016 21:00
[ 5, 6, 5, 1, 7, 3, 8, 4, 5, 8 ]
Sebastian Luberriaga
@Lube
Mar 23 2016 21:00
like that?
Jonah
@jonahx
Mar 23 2016 21:01
@Lube, no, it should be returning a function, so that your statement would need to end with (10)() to work
Sebastian Luberriaga
@Lube
Mar 23 2016 21:01
i just applied it
@ram-bot R.times(R.compose(Math.floor, R.multiply(10), Math.random));
ram-bot
@ram-bot
Mar 23 2016 21:02
[Function: f1]
Sebastian Luberriaga
@Lube
Mar 23 2016 21:02
o wait the parameter is the range
not the amount of objects
right?
Jonah
@jonahx
Mar 23 2016 21:02
no, i’m saying the (10) shoudl return a function which, when called, returns the number between 1 and 10
Sebastian Luberriaga
@Lube
Mar 23 2016 21:02
yeah I get it now
Jonah
@jonahx
Mar 23 2016 21:06
cool. i’m finding this rather humbling tbh, it seems like it should be trivially easy, as the original, non-point-free one is so simple. i’m strongly suspecting the difficulty is because of the random….
Keith Alexander
@kwijibo
Mar 23 2016 21:10
@ram-bot R.pipe(Math.random, R.multiply(10), Math.floor)()
ram-bot
@ram-bot
Mar 23 2016 21:10
8
Keith Alexander
@kwijibo
Mar 23 2016 21:13
@ram-bot R.converge(R.multiply, [R.identity, Math.random])(10)
ram-bot
@ram-bot
Mar 23 2016 21:13
7.71014136262238
Sebastian Luberriaga
@Lube
Mar 23 2016 21:13
how do you match arguments inside compose or pipe?
Keith Alexander
@kwijibo
Mar 23 2016 21:14
@Lube you have to swap things around and use converge
@ram-bot R.pipe(R.converge(R.multiply, [R.identity, Math.random]), Math.floor)(10)
ram-bot
@ram-bot
Mar 23 2016 21:14
2
Sebastian Luberriaga
@Lube
Mar 23 2016 21:14
@ram-bot R.pipe(Math.random, R.multiply(R.__), Math.floor)(3);
ram-bot
@ram-bot
Mar 23 2016 21:14
NaN
Sebastian Luberriaga
@Lube
Mar 23 2016 21:14
let me check what converge does
Keith Alexander
@kwijibo
Mar 23 2016 21:14
@ram-bot R.pipe(R.converge(R.multiply, [R.identity, Math.random]), Math.floor, R.always)(10)()
ram-bot
@ram-bot
Mar 23 2016 21:14
1
Keith Alexander
@kwijibo
Mar 23 2016 21:15
tbh I usually have to check the docs to see whether I want converge or useWith
and in practice I prefer to use arrow functions for that sort of thing
@jonahx is this what you want? R.pipe(R.converge(R.multiply, [R.identity, Math.random]), Math.floor, R.always)(10)()
Sebastian Luberriaga
@Lube
Mar 23 2016 21:18
I understand converge, but is so fugly like that
isn't it? converging on identity only to pipe multiply at the front?
Keith Alexander
@kwijibo
Mar 23 2016 21:18
i prefer x => pipe(f,g(x), h)
Sebastian Luberriaga
@Lube
Mar 23 2016 21:18
yes
definitely
But now is no longer point free
though arrow functions are not that verbose
Keith Alexander
@kwijibo
Mar 23 2016 21:21
PF is only good insofar as it is a) more declarative and readable, or, b) a fun game in ramda gitter channel
Sebastian Luberriaga
@Lube
Mar 23 2016 21:21
I completely agree with that statement
I just dont have the knowledge to figure out if there is any way to bind like this
@ram-bot R.pipe(Math.random, R.multiply(R.__), Math.floor)(3);
ram-bot
@ram-bot
Mar 23 2016 21:22
NaN
Sebastian Luberriaga
@Lube
Mar 23 2016 21:23
@ram-bot R.compose(Math.floor, R.useWith(R.multiply, [R.identity])(Math.random()))(12)
Keith Alexander
@kwijibo
Mar 23 2016 21:24
R.__ doesn't work in pipe or compose (or variadic functions in general perhaps?)
Sebastian Luberriaga
@Lube
Mar 23 2016 21:24
@ram-bot R.compose(Math.floor, R.useWith(R.multiply, [R.identity])(Math.random()))(12)
ram-bot
@ram-bot
Mar 23 2016 21:24
9
Sebastian Luberriaga
@Lube
Mar 23 2016 21:24
@jonahx with usewith
Keith Alexander
@kwijibo
Mar 23 2016 21:24
and even if it did, the R.__ is inside another function inside the pipe
Sebastian Luberriaga
@Lube
Mar 23 2016 21:25
Im not sure honestly where the issue resides
but it probably must be because compose doesn't know how to bind the placeholder
Keith Alexander
@kwijibo
Mar 23 2016 21:27
a bit cleaner if you use S.S instead of converge
@ram-bot R.pipe(S.S(R.multiply, Math.random), Math.floor, R.always)(10)()
ram-bot
@ram-bot
Mar 23 2016 21:28
TypeError: S.S is not a function
Keith Alexander
@kwijibo
Mar 23 2016 21:28
damn
Sebastian Luberriaga
@Lube
Mar 23 2016 21:28
The S combinator?
this maybe the issue with compose
Performs right-to-left function composition. The rightmost function may have any arity; the remaining functions must be unary.
Denys Mikhalenko
@prontiol
Mar 23 2016 21:29
the problem is in how currying works
Keith Alexander
@kwijibo
Mar 23 2016 21:30
R.compose(R.always, Math.floor, S.S(R.multiply, Math.random))(10)()
that doesn't read too badly
Sebastian Luberriaga
@Lube
Mar 23 2016 21:31
you are right, and if you know combinators, S combinators is almost spelled here
like a textbook example
@prontiol could you expand on that remark?
Jonah
@jonahx
Mar 23 2016 21:33
@kwijibo I haven’t tried your latest one yet, but this one didn’t work:
var rng = R.pipe(R.converge(R.multiply,[R.identity, Math.random]), 
                 Math.floor, R.always)(10)
times(rng, 10) // returns same number 10 times
same thing with this:
var rng = R.compose(R.always, Math.floor, S.S(R.multiply, Math.random))(10)
times(rng, 10) // same number 10 times
Keith Alexander
@kwijibo
Mar 23 2016 21:35
that's because of the always
Risto Stevcev
@Risto-Stevcev
Mar 23 2016 21:38
You could use R.partial
@ram-bot
R.partial(R.times(R.compose(Math.floor, R.multiply(10), Math.random)), [10])()
ram-bot
@ram-bot
Mar 23 2016 21:38
[ 0, 6, 8, 1, 2, 0, 1, 3, 5, 0 ]
Keith Alexander
@kwijibo
Mar 23 2016 21:38
or curryN
Risto Stevcev
@Risto-Stevcev
Mar 23 2016 21:39
Takes a function f and a list of arguments, and returns a function g. When applied, g returns the result of applying f to the arguments provided initially followed by the arguments provided to g.
Jonah
@jonahx
Mar 23 2016 21:39
@Risto-Stevcev How would you rewrite your partial example into var rngN = … form, where you are passing the 10 in
Risto Stevcev
@Risto-Stevcev
Mar 23 2016 21:39
Yeah but curryN can't be applied using (), the argument would need to have something like null
Keith Alexander
@kwijibo
Mar 23 2016 21:40
oh really?
Risto Stevcev
@Risto-Stevcev
Mar 23 2016 21:40
Yeah
@ram-bot
R.curryN(2, R.times(R.compose(Math.floor, R.multiply(10), Math.random)))(10)()
ram-bot
@ram-bot
Mar 23 2016 21:40
[Function]
Risto Stevcev
@Risto-Stevcev
Mar 23 2016 21:40
@ram-bot
R.curryN(2, R.times(R.compose(Math.floor, R.multiply(10), Math.random)))(10)(null)
ram-bot
@ram-bot
Mar 23 2016 21:40
[ 1, 8, 9, 3, 9, 8, 7, 6, 5, 1 ]
Keith Alexander
@kwijibo
Mar 23 2016 21:41
odd; i expect there's a good reason for it...
Denys Mikhalenko
@prontiol
Mar 23 2016 21:41
@Lube Risto-Stevcev just did this :-)
Risto Stevcev
@Risto-Stevcev
Mar 23 2016 21:42
@jonahx Like this:
@ram-bot
R.compose(R.partial(R.times(R.compose(Math.floor, R.multiply(10), Math.random))), R.of)(10)()
ram-bot
@ram-bot
Mar 23 2016 21:42
[ 5, 5, 8, 8, 7, 0, 6, 4, 3, 7 ]
Jonah
@jonahx
Mar 23 2016 21:42
the 10 is hardcoded inside the multiply there….
Denys Mikhalenko
@prontiol
Mar 23 2016 21:42
It will not work with curry, curryN or converge as a top level fn, neither with pipe nor with compose
Sebastian Luberriaga
@Lube
Mar 23 2016 21:43
But is this because of the ramda implementation
or because in theory is imposible to do
Jonah
@jonahx
Mar 23 2016 21:43
i’m now suspecting the latter.
Denys Mikhalenko
@prontiol
Mar 23 2016 21:43
well, i am not very good in fp theory
but I suspect this is correct behaviour
Sebastian Luberriaga
@Lube
Mar 23 2016 21:44
I think you could compose like this in haskell
binding inner parameters like this
Denys Mikhalenko
@prontiol
Mar 23 2016 21:44
as we expect from auto-curried function to be curried until we've got all the parameters needed
it will not return the result, when called with ()
so that's the root of the problem
afaik, the only function which works this way is always
Jonah
@jonahx
Mar 23 2016 21:50
i think it’s like this: the point free style means, bc of refential transparency, that the thing on the left and on the right can always be subsititued for one another. and also, if they are functions, lhs() == rhs() will always be true. but this won’t be the case with the random in there. so perhaps this is actually a theoretical impossiblity?
Denys Mikhalenko
@prontiol
Mar 23 2016 21:53
it is not theoretically impossible
Jonah
@jonahx
Mar 23 2016 21:53
do you have a solution?
(not that not having one proves it is impossible :) )
Denys Mikhalenko
@prontiol
Mar 23 2016 21:54
well, i guess so
gimme a minute
Sebastian Luberriaga
@Lube
Mar 23 2016 21:56
@ram-bot
R.compose(R.multiply, R.nAry(0,Math.random))()(12);
ram-bot
@ram-bot
Mar 23 2016 21:56
4.239617474377155
Denis Stoyanov
@xgrommx
Mar 23 2016 21:57
please use mancy http://mancy-re.pl/ :smile:
Sebastian Luberriaga
@Lube
Mar 23 2016 21:57
@ram-bot
var rng = R.compose(R.multiply, Math.random)(); 

R.times(rng, 10);
ram-bot
@ram-bot
Mar 23 2016 21:57
[ 0,
  0.4005843687336892,
  0.8011687374673784,
  1.2017531062010676,
  1.6023374749347568,
  2.002921843668446,
  2.403506212402135,
  2.8040905811358243,
  3.2046749498695135,
  3.6052593186032027 ]
Sebastian Luberriaga
@Lube
Mar 23 2016 21:57
I will ty @xgrommx
Jonah
@jonahx
Mar 23 2016 21:58
@Lube, you haven’t created an rngN, although i guess that proves my “theoretically impossible” argument wrong.
Denis Stoyanov
@xgrommx
Mar 23 2016 21:58
Due to it looks like a spam of results
Keith Alexander
@kwijibo
Mar 23 2016 22:05
const makeRandom = R.compose(Math.floor, S.S(R.multiply, Math.random))
const thunkify = f => x => () => f(x)
thunkify(makeRandom)(10)()
Jonah
@jonahx
Mar 23 2016 22:05
@kwijibo but that’s cheating, since thunkify isn’t point free :)
Keith Alexander
@kwijibo
Mar 23 2016 22:06
the thunkification is the weird bit, and trying to cobble it together from partial and other combinators is hard to figure out and hard to read
none of the other functions are defined as point-free
Jonah
@jonahx
Mar 23 2016 22:07
hmm… i suppose that’s true. that sort of explodes the whole premise of the question tho
Keith Alexander
@kwijibo
Mar 23 2016 22:08
well, what do you really mean by point free?
Jonah
@jonahx
Mar 23 2016 22:09
i suppose when i originally asked the question i meant “point free w.r.t the functions in ramda”, so you’d have to compose an answer purely out of them, and your solution would read like rngN = ..ramda stuff… without any parameters or fat arrows
your example shows that if we add thunkify to ramda, it can be done
still strikes me as quite odd that we need to add something, given the power of ramda
Keith Alexander
@kwijibo
Mar 23 2016 22:11
functions get added to and removed from ramda "all the time"
Jonah
@jonahx
Mar 23 2016 22:11
fair enough
Keith Alexander
@kwijibo
Mar 23 2016 22:11
so I'm not sure that being able to do something purely with ramda functions or not proves much?
joneshf-work1
@joneshf-work1
Mar 23 2016 22:12
@jonahx everything can't be point free
At some point you have to talk about the points.
compose isn't point free as implemented.
Jonah
@jonahx
Mar 23 2016 22:13
do you think having all points confined to your library is benefit? so that you’d be able to program all your application logic without them?
joneshf-work1
@joneshf-work1
Mar 23 2016 22:13
no
That leads to cryptic code
which is fine if you're in some obfuscation challenge.
Jonah
@jonahx
Mar 23 2016 22:15
i don’t think that claim is fair. the cryptic- ness level depends on how rich and expressive your base library is. often, point-free code reads much more clearly. from haskell wiki: "This style is particularly useful when deriving efficient programs by calculation and, in general, constitutes good discipline. It helps the writer (and reader) think about composing functions (high level), rather than shuffling data (low level)."
joneshf-work1
@joneshf-work1
Mar 23 2016 22:15
pointfree is fine when the solution is very direct
well sure, if you have functions for lots of different things, then you can write many things point free.
Jonah
@jonahx
Mar 23 2016 22:17
i’d argue that's the whole point of a well-designed core library
Keith Alexander
@kwijibo
Mar 23 2016 22:17
(just for fun, const thunkify = R.useWith(R.partial, [R.identity, R.of])
Risto Stevcev
@Risto-Stevcev
Mar 23 2016 22:17
@jonahx Figured it out:
@ram-bot
R.compose(R.partial(R.times(R.compose(Math.floor, R.converge(R.multiply, [R.identity, Math.random])))), R.of)(20)()
ram-bot
@ram-bot
Mar 23 2016 22:17
[ 0, 0, 1, 1, 0, 2, 2, 2, 6, 2, 6, 10, 9, 6, 13, 6, 2, 5, 14, 6 ]
joneshf-work1
@joneshf-work1
Mar 23 2016 22:17
surely, you can't think that's better than a pointful version.
Risto Stevcev
@Risto-Stevcev
Mar 23 2016 22:18
of course not
Jonah
@jonahx
Mar 23 2016 22:18
ofc not, but at this point it’s just a challenge :)
Risto Stevcev
@Risto-Stevcev
Mar 23 2016 22:18
but he was asking for a pointfree one
yea
joneshf-work1
@joneshf-work1
Mar 23 2016 22:18
Sorry, that was directed at @jonahx.
Jonah
@jonahx
Mar 23 2016 22:19
@joneshf-work1 obviously i am sticking with point version, but i just wanted to see how it would be done
Keith Alexander
@kwijibo
Mar 23 2016 22:19

@jonahx

let thunkify = f => x => () => f(x)

vs

let thunkify = R.useWith(R.partial, [R.identity, R.of])

?

Jonah
@jonahx
Mar 23 2016 22:19
@Risto-Stevcev thanks, i am checking out out your solution now
@kwijibo obv former is better
joneshf-work1
@joneshf-work1
Mar 23 2016 22:20
@jonahx right, I get it was an exercise.
Keith Alexander
@kwijibo
Mar 23 2016 22:21
is anyone feeling eloquent enough to explain why the former is better?
is it because it only contains 2 concepts: functions, and values?
joneshf-work1
@joneshf-work1
Mar 23 2016 22:22
basically.
Keith Alexander
@kwijibo
Mar 23 2016 22:22
whereas the latter has ~5
Jonah
@jonahx
Mar 23 2016 22:22
@kwijibo other than the obvious brevity, i’d also say that the structure of what’s happening is physically contained in the text.
and yeah, number of concepts too
joneshf-work1
@joneshf-work1
Mar 23 2016 22:22
yeah, that too
Keith Alexander
@kwijibo
Mar 23 2016 22:23
yeah maybe that, but is that just a syntax thing?
Jonah
@jonahx
Mar 23 2016 22:23
i strongly contest the premise that syntax is “just syntax” :)
Keith Alexander
@kwijibo
Mar 23 2016 22:23
fair enough
syntax goes a long way
Jonah
@jonahx
Mar 23 2016 22:25
if you look at the first one, it’s hard to imagine distilling the underlyig concept very much further.
so i think there’s an always an element of “you’re done when there is nothing else to take away” in readable code
Keith Alexander
@kwijibo
Mar 23 2016 22:26
I think it's because the 2nd one is being meta; it's using javascript to describe javascript
like RDF reification, or describing the structure and contents of a json document, in json
anyway, night all
Jonah
@jonahx
Mar 23 2016 22:28
that’s true, and at bottom we’re talking about concepts that are language agnostic. the first version is close to an efficient textual representation of the concept. the second uses text (and verbose, poorly named text at that) to describe teh concepts, or as you’re saying the js that describes them
night
Denys Mikhalenko
@prontiol
Mar 23 2016 22:39
R.converge(R.partial, [R.wrap(R.pipe(R.converge(R.multiply, [Math.random, R.identity]), Math.floor), R.identity), R.of])
Denis Stoyanov
@xgrommx
Mar 23 2016 22:43
holy shit!
Aldwin Vlasblom
@Avaq
Mar 23 2016 22:43

So which future library should we use, LazyEither, Folktale, Fluture, Fantasy-Future? Anyone has an article on that? -- @Lube

I'm keeping a wiki page on the notable differences between commonly discussed implementations.

Jonah
@jonahx
Mar 23 2016 22:43
lol, yeah, @Risto-Stevcev version is maybe a tiny bit shorter, but similar
@prontiol thanks for your answer
@Avaq that’s helpful for me, thanks
If I want to import all of ramda into a module, is there a better way than this:
var R = require('ramda');

// pollute current namespace with ramda
for (var key in R)
  global[key] = R[key]
Denys Mikhalenko
@prontiol
Mar 23 2016 22:49
Object.assign?
Jonah
@jonahx
Mar 23 2016 22:50
so just Object.assign(global, R)?
Denys Mikhalenko
@prontiol
Mar 23 2016 22:50
yep
Jonah
@jonahx
Mar 23 2016 22:50
cool
Jonah
@jonahx
Mar 23 2016 22:56
@prontiol the key concept i was missing was partial. based off you version, i got it a bit shorter (still obv ugly):
var rng20 = pipe(of, partial(pipe(useWith(multiply, [identity, Math.random]))))(20)
times(rng20, 10)
Risto Stevcev
@Risto-Stevcev
Mar 23 2016 23:00
@prontiol nice
This exercise was good practice
Jonah
@jonahx
Mar 23 2016 23:01
Yeah, frustrating but helpful as i’m just learning ramda, and planning to use it on my next project
thanks all for your help
Risto Stevcev
@Risto-Stevcev
Mar 23 2016 23:12
@Avaq I plan on improving the performance (it's based on the ramda fantasy implementation which your benches show is slow), as well as utilities, nice errors and throw safety fairly soon. I'll keep you updated
Aldwin Vlasblom
@Avaq
Mar 23 2016 23:13
@Risto-Stevcev Cool! :D
Risto Stevcev
@Risto-Stevcev
Mar 23 2016 23:13
I like that your implementation has this: .fold(Left, Right).value(continuation).
:)
Because it's also flexible and you could use Maybe instead if you don't care about the error
Definitely a solid Future implementation
Aldwin Vlasblom
@Avaq
Mar 23 2016 23:16
Thanks :blush:
It was your LazyEither which inspired the .value syntax
Denys Mikhalenko
@prontiol
Mar 23 2016 23:27
@jonahx your shorter version does not work as expected unfortunatelly
Aldwin Vlasblom
@Avaq
Mar 23 2016 23:27
@Risto-Stevcev I think much of the performance of your lib is lost by using Sanctuary's full type checking. It might be worth switching to require('sanctuary').unchecked if you're going to perform your own type checking anyway. :)
I wouldn't worry that much about performance though until everything is exactly how you envisioned it.
Denys Mikhalenko
@prontiol
Mar 23 2016 23:28
This message was deleted
@ram-bot
pipe(of, partial(pipe(useWith(multiply, [identity, Math.random]))))(10)()
ram-bot
@ram-bot
Mar 23 2016 23:29
[Function]
Denys Mikhalenko
@prontiol
Mar 23 2016 23:29
but this should be actually a random number from from 0 to 9
Risto Stevcev
@Risto-Stevcev
Mar 23 2016 23:38
@Avaq Nice, I'll try that. The lib was actually a response to a ramda issue which branched off into a discussion about how fork feels awkward. Yeah, I think I'll add the new features first
But I do think it also has something to do with being similar to the ramda implementation
Mick van Gelderen
@mickvangelderen
Mar 23 2016 23:42
@Risto-Stevcev I'm interested in why fork feels akward, where can I find the discussion?
Risto Stevcev
@Risto-Stevcev
Mar 23 2016 23:53
ramda/ramda#1665
I wrote that when I was still pretty new to ramda, though some of my points still hold
I also wrote LazyEither as an example to @arcseldon as part of the thread, and I haven't yet implemented everything I'm envisioning for it because I've been tied up with other stuff
The crux of it is that there's no reason to force the user into having to supply two callbacks. It could simple supply one callback with two parameters, and the user can then decide to branch that off into multiple callbacks if s/he wishes to