mostlyadequate / mostly-adequate-guide Goto Github PK
View Code? Open in Web Editor NEWMostly adequate guide to FP (in javascript)
License: Other
Mostly adequate guide to FP (in javascript)
License: Other
//not pointfree because we mention the data: word
var snakeCase = function (word) {
return word.toLowerCase().replace(/\s+/ig, '_');
};
//pointfree
var snakeCase = compose(replace(/\s+/ig, '_'), toLowerCase);
Noticed that split
is missing from https://github.com/DrBoolean/mostly-adequate-guide/blob/master/ch4.md#exercises
var split = _.curry(function(splitter, str) {
return str.split(splitter);
});
@DrBoolean Hi Brian! I've read and loved the guide very much. I would love to translate it to russian, can I do that?
For 2 reasons actually :)
(compose(f, id) == f) === false
because even though functions are equivalent, JavaScript can't detect that equality.compose(f, id) == f
evaluates to false
we are comparing compose(id, f)
and false
, which is also false
.This line might be correct as a pseudo code, but presence of var
suggest that it's a JavaScript code.
Shoudn't the following be the other way round?
var fs = require('fs');
// scary
fs.readFile('freaky_friday.txt', Db.save);
// less so
fs.readFile('freaky_friday.txt', Db.save.bind(Db));
In chapter 8 I found this type signature:
map :: Functor f => (a -> b) -> f a -> f b
While the introductory chapter on Hindley-Milner was easy to follow, I think it doesn't cover the above signature. The problem is the f a
and f b
part. What does that mean? f
multiplied by a
? Or f
applied to a
? Or something else? Maybe you could elaborate on this a little.
Oh, and by the way, thanks for your impressive work so far. It's a joy to read and I like how concise you present the main principles.
shouldnt curry be defined as
var curry = require('lodash').curry
and not as
var curry = require('lodash.curry')
I downloaded the epub version from https://www.gitbook.com/download/epub/book/drboolean/mostly-adequate-guide and when I open it in Mac iBooks v1.2 I get an error telling me the book is corrupt.
I've done reading all available chapters in this book and I think that this is the best available guide about FP ever. But the problem is that like others it miss real world examples.
I'm requesting to add chapter with an implementation of some non trivial application, that uses all the staff explained in this books. I suggest to use more complex example than flickr.
In Chapter 7, it would be helpful to give a worded explanation about the reduce signature
// reduce :: (b -> a -> b) -> b -> [a] -> b
I think the answer to exercise 2 of the monads chapter is different when running on Windows due to the path separators.
Answer given is:
var ex2 = _.compose(chain(_.compose(pureLog, _.last, split('/'))), getFile);
But for me the test fails like this:
AssertionError: expected 'logged D:\\Users\\jschreuder\\Documents\\mostly-adequate-guide\\code\\part2_exercises\\exercises\\monads\\monad_exercises.js' to equal 'logged monad_exercises.js'
I had to split on \
to get the test to pass:
var ex2 = _.compose(chain(_.compose(pureLog, _.last, split('\\'))), getFile);
Might be worth switching up this exercise with some other task to avoid cross platform issues?
What you think about rewriting all the code examples in the book in ES6? It feels more functional than ES5.
Is it Professor Frisby (per the cover) or Risby (chapter 1)? Or is the cover meant to be a portmanteau of F. Risby? Is it intended to create an air of mystery? Is he actually a Professor?
I'm going to sit here and be quiet now.
What license is this guide under? If you aren't sure, I recommend any of the free culture licenses.
It would help if you would explain why the non-functional example got the wrong answer. I'm not sure what it is trying to do. It would help if you had an English explanation of what conjoin and breed are supposed to do, since they seem (to me) to simply be doing the wrong thing, if you are really treating it as a simplified representation of the real world, rather than purely abstract mathematical operations.
In trying to process it, I started writing out something like this (which might be nice to have in your writeup)....
First, I expanded this:
flock_a.conjoin(flock_c).breed(flock_b).conjoin(flock_a.breed(flock_b))
to this:
flock_a.conjoin(flock_c)
// flock_a: 4, flock_c: 0
flock_a.breed(flock_b)
// flock_a: 4, flock_b: 2
flock_a.breed(flock_b)
// flock_a: 8, flock_b: 2
flock_a.conjoin(flock_a)
// flock_a: 32
Then, in English:
flock_a.conjoin(flock_c)
flock a, which has 4 seagulls, joins with flock c, which has 0 seagulls, which causes flock a to have its seagulls increased by the number of seagulls in flock b, resulting in flock a still having 4 seagulls and flock c unaffected, still having zero seagulls.
flock_a.breed(flock_b)
Then flock a (still 4 seagulls) breeds with flock b, which has 2 seagulls. This results in flock a's number of seagulls being multiplied by the number of seagulls in flock b, leaving b unaffected. Flock a will now have 8 seagulls, with b still having 2 seagulls.
flock_a.breed(flock_b)
Now flock a (having 8 seagulls) again breeds with flock b (having 2 seagulls). This results in flock a having 16 seagulls, and flock b remains with 2 seagulls.
flock_a.conjoin(flock_a )
Now flock a conjoins with itself, doubling its numbers to 32.
I notice that conjoin does not set other.seagulls to zero (you are adding the seagulls to another flock, but still leaving them in their existing flock), and breed does not add the product of breeding to the existing seagulls (i.e. all the parents in "this" are killed by the breeding). Also it seems wrong to use multiplication at all to represent breeding in this way, it doesn't make sense. These seem to be logical errors, but are somewhat masked by other things (such as having flock_c having zero members to begin with).
However, it seems the most egregious error is from having a flock conjoin with itself, which doubles its numbers.
Without any sort of English translation or other breakdown of what you are trying to do, it is extremely difficult to tell what it is.
At least with the OO approach, I can figure out what the answer actually represents (the number of seagulls in flock a), and see why it seems to be incorrect. With the functional approach, "result" may be what you consider correct, but I have no idea what that value actually represents.
The books seems to be great, but the format without good navigation is bad. Instead of using simple markdown files the GitBook format could be used.
What do you think about renaming map
to fmap
for functors. So we can eliminate confusion in this. The point is that map usually used to map collections, not single values.
See this jsbin
lastUpper as written in tutorial doesn't work unless you group the functions as in lastUpper2 example, or did you modify the compose function?
Perhaps it just a place holder for a chapter that does not yet exist, but chapter 10 is not in the repo although chapter 9 contain a link to it.
The IO functor in chapter 8 is not working for me. When I console.log the output, I just get IO{}
edit: I just figured out that I needed to call __value() to get the result
Not sure if it's wrong, or I misunderstood something.
Here is the quote:
What are some other categories, you ask? Well, we can define one for directed graphs with nodes being objects, edges being morphisms, and composition just being path concatenation. We can define one for Numbers with
>
as morphisms and 0 as identity[^actually any partial or total order can be a category]. There are heaps of categories, but for the purposes of this book, we'll only concern ourselves with the one defined above. We have sufficiently skimmed the surface and must move on.
I think the morphisms in a partial order with numbers should be <=
, the identity should be also <=
(any number less or equal to itself), and 0
could be the initial object (in case we don't include negative numbers).
Please check definition of partial order from Categories Great and Small, and of initial object from Products and Coproducts โ chapters from "Category Theory for Programmers" by @BartoszMilewski
A good addition to the book would be to add this glossary: functional-programming-jargons
Reopened from #149. Any thoughts/ideas on how to coordinate it?
Would you like some help adding semicolons to your code examples, or do you feel strongly about not using them? I notice you tend to leave them out in a lot of cases, but it's done inconsistently, so I wasn't sure what your intention was.
I'm new to the pointfree concept, so I'm wondering if I'm misunderstanding, or reading too deeply into the definition of pointfree.
It means functions that never mention the data upon which they operate.
var fastestCar = _.compose(append(' is the fastest'),
_.prop('name'),
_.last,
_.sortBy(_.prop('horsepower')))
In this example, the function is aware of the argument's structure (properties name
and horsepower
), is this still considered pointfree?
in line:
var jobe = Immutable.Map({name:"Jobe", hp:20, team: "red"})
var Immutable = require('immutable');
is missing from code
maybe better to use Set, since is implemented in all modern browsers?
I understand the benefits of having synchronous functions be pure functions (in any language), and I think I understand the necessity of having pure monadic functions in Haskell and having the runtime manage the side effects.
But within JavaScript's semantics, I don't understand how making future-returning functions to be pure would be beneficial.
Consider this snippet. f()
is pure and g()
is not:
var Promise = require('Promise');
// f :: a -> _ -> Promise Error Number
function f(a) {
return function(){
return new Promise(function(resolve){
resolve(localStorage.getItem(a));
});
}
}
// g :: a -> Promise Error Number
function g(a) {
return new Promise(function(resolve){
resolve(localStorage.getItem(a));
});
}
f()
returns a thunk of a Promise. It doesn't do any execution unless we run the thunk. So it almost acts like a Task from 'folktale/data.task'. g()
on the other hand starts executing right away.
Now, what's the practical benefit of using f()
instead of g()
? Because, the thunk returned from f()
doesn't really give us any insight into what it plans to do. And we're planning to run that thunk right away anyway.
Or put another way: If we were to use a function like f()
or g()
as an action creator in a flux architecture, how would we benefit from using f()
? What features/assurances/properties would it make possible?
Btw, I'm intentionally not using data.task
here because my question is not about the benefits of using Tasks instead of Promises. It's only about having pure future-returning functions vs impure ones.
Hey Brian! I noticed a couple lodash mentions and thought you'd dig lodash-fp since its the auto-curried iteratee-first data-last flavor of lodash which fits a bit more with the text.
After adding the ePub to iBooks, either downloaded from Gitbook or built manually, on open, iBooks reports that "This file is corrupt" and closes the book for reading.
Are there any plans to generate one PDF out of this files? It'll be handy to have it also in such format
Thanks for this awesome book - and kudos for picking up JS for teaching FP! I have two quick suggestions that might help -
[1, 2, 3, 4, 5].map((x) => x * x).reduce((a, b) => a + b)
Please share your thoughts on what you think about these ideas. Lastly, I'll be more than happy to send in PRs if you prefer.
Thanks again for writing this wonderful guide! ๐ป
Disclosure: I'm in no way associated with Gitbook; just a happy reader.
Requesting to add tests for exercises in the book. So people can test their solutions against these tests.
Hi!
Its duplicating of my email via gitbook.
In Chapter 6 following function seems to be impure:
var img = function (url) {
return $('<img />', { src: url });
};
As I've understood from previous chapters img
is impure because it relies on $
which isnt directly passed via arguments. But in the text there is nothing about img
impurity so some folks can assume its pure.
Am I right?
Hello, thanks for great book: eyes opening!
I've got a doubt concerning Maybe.join
(thus Maybe.chain
).
When I do f(a).map(g)
Given:
f :: a โ Maybe(a)
g :: a โ Maybe(a)
Then:
f
and g
succeeds, return type is Maybe(Maybe(a))
f
fails, g isn't executed, return type is Maybe(null)
So when I join
I either have a Maybe(a)
or null
... which isn't convenient for later processing.
I fixed it by writing: Maybe.join = isNothing() ? Maybe.of(null) : __value
Is that correct? Is there something else I missed or a better way?
Thanks!
Chapter 3 reads "Since our data is immutable, we can simply replace the teams with their actual value", and then the isSameTeam is removed. Doesn't that change the api, as teammates can now punch each other?
Would be nice to have answers to the examples for each chapter. Hard to know if one has grasped the concept without knowing if we've completed the example correctly!
Also, this is great stuff!! Thank you!
In chapter 8, the part concerning withdrawing from an account using maybes, uses two functions I can not find being defined anywhere. The line:
var finishTransaction = compose(remainingBalance, updateLedger);
Were those functions left out with the intent to not run this code or were they left to the reader to implement?
/*
Exercise 4:
============
Write a function: sanitizeNames() using compose that returns a list of lowercase and underscored
names: e.g: sanitizeNames(["Hello World"]) //=> ["hello_world"].
*/
sanitizeNames
expects an array of objects not strings
"Now it is seen a function" needs to be "seen as a function".
When the filterQs
function from Curry Exercise 2 is applied to test array (['quick', 'camels', 'quarry', 'over', 'quails']
) it returns the same array unchanged.
The cause of this is the match
function which is used by filterQs
. It returns empty array when the regexp and string to which it is being applied does not match each other. Empty array is considered truthy, so the Ramda's filter
does not filter anything.
I made provided test pass by changing the function to this:
var filterQs = _.reject(_.compose(_.isEmpty, _.match(/q/i)));
But this is probably not the best way to solve the exercise, as composition is subject of the next chapter.
As possible solution, the Ramda's match
can be replaced with the match
from the support
module, so the exercise will look like this:
// Exercise 2
//==============
// Refactor to remove all arguments by partially applying the functions
// Use the provided _match function
// LEAVE BE:
var _match = _.curry(function(what, x) { return x.match(what); });
// REFACTOR THIS ONE:
var filterQs = function(xs) {
return _.filter(function(x){ return _match(/q/i, x); }, xs);
};
var dasherize = compose(join('-'), map(replace(/\s{2,}/ig, ' ')), split(' '))
I can't think of any useful application ๐
Hi,
i have just started to read your book, thank you for it !
However, i was a bit disturbed by the example of oo code you wrote in the "flock of seagulls" part.
This part is supposed to show some advantages of FP over OO, but i think it's demonstrating how NOT to code in OO !
As you wrote :
Contrast this with our absurd object version which ignores built in functions and data types in order > to model the "real world".
I don't know what it's supposed to model, but it's not the real world !
You are instantiating three Flock objects, each with an initial size. I'm not sure instantiating a zero sized flock (as you did with flock_c) means anything in real life, i probably would have forced the constructor to throw when passing zero as a parameter or to return undefined, but that's not the important point there.
The big problem comes from the actual heart of the program, as you call it the "ghastly abomination".
Here it is :
var result = flock_a.conjoin(flock_c).breed(flock_b).conjoin(flock_a.breed(flock_b)).seagulls;
What the heck is this supposed to mean ?
What real world problem is this supposed to represent ?
Let's try to understand:
flock_a join with flock_c (remember, it's a zero sized flock, if that mean anything).
So the flocks are joining, they becomes one only flock, the members of flock_c are coming into flock_a or the opposite. Is it not a very good case for state modification ? What does it mean to take two flocks as parameters and get a new flock ? Does it really make sense to make the flocks immutable ? After flock_c has joined flock_a, is there still a flock_c ? Can we say that flock_a has not changed ? (actually it has not since flock_c has zero members, but let's pretend the opposite).
Is the adding of flock_c members really an addition of two immutable value or the mutation of flock_a state and the disapearance of flock_c ?
I vote for this last propostion, and i write this operation like this :
flock_a.conjoin(flock_c);
How could this be simpler ?
I could even add a method on Flock that would put the population at zero when consumed by Flock.conjoin()
Let's do that.
Flock.prototype.empties = function(){
this.seagulls = 0;
}
And modify conjoin accordingly:
Flock.prototype.conjoin = function(other){
this.seagulls += other.seagulls;
other.empties();
}
And we don't return this, because it violate the law of demeter and makes your code a mess.
It seams that the result of flock_a joining force with flock_c, which we choose to still call flock_a, is now doing some breeding business with flock_b.
From your defintion in the class, we understand that after a round of reproduction, the child stay with the object whose breeding method was called and the other parent which continue on his own doesn't keep parental authority. So the flock_a population is again modified, and flock_b population doesn't change. Easy !
flock_a.breed(flock_b);
How could this be simpler ?
Now the resulting flock (again flock_a) is joining with the result of flock_a.breed(flock_b) which is also flock_a.... Wow wow wow, stop where you are ! What does it mean in the real world to join a flock with itself ??? I don't get it, are you just counting seagulls or are you breeding/joining them ?
Ok, perhaps i have not understood the problem correctly, when two flocks breed, they are producing a completely new Flock and are themselves unmodified.
But then the breed method is not correct and should be rewritten like this:
Flock.prototype.breed = function(other) {
return new Flock(this.seagulls * other.seagulls);
};
And that changes everything !
Let's reconsider each steps with this new information.
Step1
flock_a.conjoin(flock_c); // no change flock_a = 4
Step2
flock_d = flock_a.breed(flock_b) // flock_d = 8; flock_a = 4; flock_b = 2;
Step3
//flock_a.breed(flock_b) return a new Flock which size is 4x2 = 8
//which will then join with flock_a
flock_a.conjoin(flock_a.breed(flock_b)) // flock_a = 8 + 4 = 12; flock_b = 2
Now it's finished, we have three discrete flocks, flock_a whose size is 12, flock_b whose size is still 2, flock_d of size 8 (and we still have the fantom flock_c of size 0).
Now, let's enjoy some functionnal programming ;)
var total = [flock_a, flock_b, flock_c, flock_d].reduce(function(total, flock){
return total + flock.seagulls;
},0);
Which gives us a total of 22 ! Not 16, not 32, 22 ! Unless i made a mistake.
I still think that good OO is very handy when modeling reality.
Complete source code to be sure we are on the same page :
var Flock = function(n) {
"use strict";
this.seagulls = n;
};
Flock.prototype.empties = function(){
"use strict";
this.seagulls = 0;
};
Flock.prototype.conjoin = function(other){
"use strict";
this.seagulls += other.seagulls;
other.empties();
};
Flock.prototype.breed = function(other) {
return new Flock(this.seagulls * other.seagulls);
};
var flock_a = new Flock(4);
var flock_b = new Flock(2);
var flock_c = new Flock(0);
flock_a.conjoin(flock_c);
var flock_d = flock_a.breed(flock_b);
flock_a.conjoin(flock_a.breed(flock_b));
var total = [flock_a, flock_b, flock_c, flock_d].reduce(function(total, flock){
return total + flock.seagulls;
},0);
console.log(total);
Thank you for your attention !
It would help a lot, if you could add a note about ramda and what it does before the exercise in chapter 4. I was quite confused, because I didn't know that split()
etc. are provided as curried functions.
ch2.md:109 equates the verbose and concise versions of BlogController
. But in the verbose version, won't index
be run with a "this"-context of Views
, whereas in the concise version, index
would be run in the "this"-context of BlogController
?
That, then, is an important difference, particularly if Views.index
uses any state of "this". While that may not fit the vision for functional programming, it's likely a reality that BlogController must accommodate in the broader world of mixed-paradigm programming.
This general concept is acknowledged later, but perhaps the caveat should be acknowledged sooner?
I can't find an implementation of reduce()
that this line of code works with from your example:
var reverse = reduce(function(acc, x){ return [x].concat(acc); }, []);
Is it a typo or can you provide a link that would explain how this reverse function operates?
And, to demonstrate my further confusion, shouldn't reverse
be in the form of a composable pure function with one argument, as in reverse=function(x) {..}
in order to be used with compose
?
Prior definitions casually used throughout is causing confusion - especially in the case of compose
where we define a non variadic version and then proceed to use the variadic one for the rest of the book.
Should replace as much as possible with ramda or lodash-fp
Hi
[@DrBoolean - Thank you. Great content and (for my taste) an eminently readable style. It is rare for technical book to make me chuckle.]
In Chapter 8 I am battling to figure out how connectDb
has a type of Config -> Either(Error, IO(DbConnection))
given that Postgres.connect
only has a type of Url -> DbConnection
.
How does the DbConnection
become an IO(DbConnection)
?
Am I confused or is there an IO.for
missing somewhere ?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.