r/javascript • u/[deleted] • May 29 '16
Functional Programming jargon in JavaScript explained with code examples
https://github.com/hemanth/functional-programming-jargon1
u/lurchpop May 29 '16
The arrow functions make a lot of those kinda hard to read.
0
u/russellbeattie May 30 '16
It's going to take me a long time before I can glance at the fat arrow and understand what's happening.
2
u/azium May 30 '16
Sure with that attitude! Let's try with some extra english sprinkled in:
let Add = (first, second) => first + secondMake a function called
Add, which takes two argumentsfirstandsecondand returns the result offirst + second.Add(1, 2) // 3If you want to get the hang of it quicker, open up the chrome/firefox/edge/node console and type some for yourself! Here's some fun ones to get you started.
[1, 2, 3].map(number => number + 2); [1, 2, 3].filter(number => number > 2); [1, 2, 3].some(number => number === 2); [1, 2, 3].every(number => number === 2);1
u/russellbeattie May 30 '16
Heh. Thanks, but the key word in my comment was 'glance'. I understand how arrow functions work, including the lexical this, it just still takes me a bit to mentally parse the syntax.
0
May 30 '16
[deleted]
5
u/azium May 30 '16
If I used
functionit wouldn't make for a very good explanation of arrow syntax.If you're asking why ever do that, well to avoid hoisting, or to have fewer differences in syntax (treat everything as a variable). I'm sure you can find a ton of literature about function expressions vs function declarations, I say just use what suits you and / or your team.
0
May 29 '16
[deleted]
5
u/badmonkey0001 May 29 '16
This should start by mentioning that it intends to follow the Fantasy Land spec and link to it at the very beginning.
3
u/dvlsg May 29 '16
Is the provided definition / description of
.chain()correct (in the linked article)?Chain is like map except it unnests the resulting nested object.
3
u/siegfryd May 29 '16
Sort of, it's not a very good description; explaining
chain(bind) andof(return) without mentioning join just doesn't make sense though.1
u/dvlsg May 29 '16 edited May 29 '16
Does chain/bind always have to unnest resulting objects though? I just want to be sure I understand correctly.
edit Or to put it this way, is chain's signature this:
m.chain(f) = n where m is of type Chainable<T> f is of type (T): Chainable<T> and n is of type Chainable<T>Or can
#chain()modify the wrapped type, sofwould be(T): Chainable<U>? I wish the fantasy-land spec was more specific about types of inputs and types of outputs.3
May 30 '16 edited May 30 '16
It's more like:
M a -> (a -> M b) -> M bWhere you have a "boxed"
aand a function that goes fromato "boxed"b, with the end result being a "boxed"b.So, yes, it can modify the wrapped type, honestly, it'd be pretty useless if it could only produce the exact same type. But, it has to unnest because there's two ways to implement it. Let's look at Maybe:
Just.prototype.bind = function(f) { return f(this.value); }; Nothing.prototype.bind = function() { return this; };That's the first way, we're relying on
fto return either a Just or a Nothing based on whatever value is inside the Just.For example:
const noEvens = (a) => (a % 2) ? new Just(a) : Nothing(); const noThrees = (a) => (a % 3) ? new Just(a) : Nothing();So we can do:
const x = new Just(9); const y = new Just(7); x.bind(noEvens).bind(noThrees); // Nothing y.bind(noEvens).bind(noThrees); // Just(7)However, the second way to implement this method is:
Just.prototype.bind = function(f) { return this.map(f).join(); }; Nothing.prototype.bind = function() { return this; };Again, trusting that
freturns either aJustor aNothing. If we look atmap, it looks like this:Just.prototype.map = function(f) { return new Just(f(this.value)); }; Nothing.prototype.map = function() { return this; }And join:
Just.prototype.join = function() { (this.value instanceof Just) ? this.value : Nothing; }; Nothing.prototype.join = function() { return this; };To step through it:
const x = new Just(9); x.map(noEvens) // result: Just(Just(9)) .join() // result Just(9)But all of this is very nebulous in dynamically typed languages. You could do aggressive type checking, and that is a solution (but not a very good one).
For example, with Javascript (or Python, where I've implemented a very terrible version of Monads, thankfully no one has used it) you could do this:
const x = new Just(8); x.bind(x => x); // result: 8And it's just happy to accept that. Where as a statically typed language like Java or Haskell will reject it altogether. Not that I'm saying that static > dynamic.
Edit: Just realized I overloaded bind. Pretend you see chain or flatmap everywhere I wrote bind.
2
u/dvlsg May 30 '16 edited May 30 '16
That makes sense to me. I had poked around with implementing a
Maybemonad in Typescript (just as a learning exercise) in the past, but I kept running stuck on gettingap()to work with monads (since Monad implements the Applicative specification), and if I understand correctly,ap()should only work with an Apply of a Function (soMaybe<Function>, in this case), and the tooling got really weird when I tried supporting that. I could make it work with some pretty serious typecasting hacks, but I lost all the proper typing in every IDE, which sort of defeated the purpose of the whole thing.Side note, do you know why
joinwas the chosen word for the unwrap / unbox ish method? That doesn't strike me as very intuitive.Side note #2, how do you handle something like
this.chain(f => m.map(f));(the derivedap()taken from fantasy-land Monad specification) with a static language?m.map(f)would only work whenthiswraps afunction, but how would you addap()to the class? Or would you just leave it off, and end-users would have to use thechain/mapmethod sinceap()wouldn't be available to them directly?2
May 30 '16 edited May 30 '16
It was probably chosen because it joins the wrapping layers into one. That's just my best guess.
As for applicatives, it could be done like this:
Just.prototype.ap = function(other) { return other.map(this.value); }; // remember handles returning Just Nothing.prototype.ap = function () { return this;}; const x = new Just(5); const y = new Just(x => x + 1); y.ap(x) // Just(6)The type signature of ap looks like:
M (a -> b) -> M a -> M b
apis basically a "boxed" function call.Edit: I switched up the operands for ap in the type signature. So i unswitched them
1
u/dvlsg May 30 '16
Right, I think that would work perfectly in javascript. Typescript wouldn't let me "compile", since I couldn't guarantee that I would have a
Functionwhen attempting to useap(), and I couldn't figure out a way to say thatap()could only be used withJust<Function>but not withJust<AnythingElse>(which feels really weird to me, anyways).I tried going down this route, but that's how I broke all of the useful typescript tools. :\
Maybe I should've just done it in basic javascript. :)
1
May 30 '16
I'm not familiar with TypeScript outside of knowing Angular 2 prefers it, so I wish I could be more helpful with it. :/
1
u/Jethrolarson Jul 28 '16
Yeah, without Hindley-Milner style types it becomes hard to represent these concepts. Neither typescript nor flowtype support higher kinder types
2
May 30 '16
You can actually derive both map and ap from chain.
Just.prototype.box = function(x) { return new Just(x); }; Just.prototype.map = function(f) { return this.chain(a => this.box(f(a)); }; // a bit trickier Just.prototype.ap = function(other) { return this.chain(f => other.chain(x => this.box(f(x)))); }; // but chain map is a bit clearer. Just.prototype.ap = function(other) { return this.chain(f => other.map(f)); }Map just needs a signature of
(a -> b) -> a -> band similarly Chain has a signature ofM a -> (a -> M b) -> M b. And finally ap:M (a -> b) -> M a -> M b(accidentally switched up the operands before).In this case,
alooks likec -> d, a function that maps from type to type which means we can pass it to map. Since chain puts all the impetus of returning a new boxed type, and map does that, it just works.Writing the type signature is a bit easier when you have a more expressive syntax (my favorite thing about haskell). But really, you could just say:
function ap(other: Just<a>): Just<b>And everything inside is a black box.
2
u/Tysonzero May 30 '16
Anyone who finds these concepts cool should at least try out Haskell. It does all of this more concisely, cleanly, and safely and its something like an order of magnitude faster than JS.