Skip to content

Instantly share code, notes, and snippets.

@CrossEye
Last active January 31, 2020 14:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save CrossEye/c73ea3600b1f7f223a23 to your computer and use it in GitHub Desktop.
Save CrossEye/c73ea3600b1f7f223a23 to your computer and use it in GitHub Desktop.

Technical Review of Functional Javascript

Review by Scott Sauyet

Functional JavaScript
Michael Fogus
O'Reilly, 2013
987-1-449-36072-6

I was very excited when I heard this book was coming out. I'd enjoyed Braithwaite's JavaScript Allongé, but that had a much more limited focus. I've been spending a great deal of time investigating functional techniques in Javascript and have been frustrated by how little has been written about these techniques. (Most articles discussing functional programming in Javascript talk about first-class functions and callbacks as major breakthroughs.) So when the author of Lemonad, one of the most interesting of the Functional Javascript libraries, was publishing the first general-purpose book on the topic, I was very interested.

I was disappointed, though, with the editing of this book. O'Reilly has certainly earned its place as one of hte top technical publishers in the world, and I was quite surprised by the quality of this book. I felt there were too many mistakes that shouldn't have gotten past technical review, and too many bad decisions that someone should have talked the author out of. I'm wondering if the technical editing was too focused on people who understand functional programming and too little focused on those with a deep understanding of Javascript.

I'm hoping that a subsequent edition can correct some of the flaws I've noticed.

I've spent a fair bit of time giving the book a careful second reading, taking copious notes as I went, addressing the author directly. The review below is mostly a trancsription of those notes, with no further general editorial opinion until the concluding section.

Preface

p.xiii: Strike 1: First words: "What is Underscore?" While Underscore is a good library and very useful, there are good arguments to be made against it being particularly functional. If nothing else, the parameter order makes it more difficult to work with. Brian Lonsdorf's video Hey Underscore, You're Doing It Wrong! gives a good overview of the reasons, but as the book goes on they become clear, too.

Moreover, including such a big library in what is really the first major publication of a functional Javascript book does a disservice to the communicy by hiding the implementation of core ideas behind a magic genie.

Chapter 1

p.1: "The Case for JavaScript". Really? For whom? In an Intro to Javascript book, sure, but here, this is totally unnecessary.

p.3, footnote 3: Don't try to weasel out of it. 😄 Everyone in the community knows that the language has its flaws!

p.4: "Why Functional Programming Matters". Come on, I've seen your blog post mentioning John Hughe's seminal paper. Here it doesn't get even a footnote?

p.7, ¶3: "That JavaScript supports [both functional and Object-Oriented] models means that systems can and should be composed of both." (emphasis added) "Can", sure, but "should" needs much more of an argument than a throw-away line, unless you mean to extend it to plain imperative models too. Even adding "arguably" would improve this tremendously. But really, I think you need more backup for this.

pp.14-15:

 function lessOrEqual(x, y) {
     return x <= y;
 }
 function comparator(pred) {
     return function(x, y) {
         if (truthy(pred(x, y)))
             return -1;
         else if (truthy(pred(y, x)))
             return 1;
         else
             return 0;
     };
 }
 [100, 1, 0, 10, -1, -2, -1].sort(comparator(lessOrEqual));

Two things: First, truthy is entirely irrelevant here, since you're dealing with a predicate. This is minor, and harmless, although you don't define truthy until page 19. But second, and more imporrant, this should really be just less, not lessOrEqual. While this does not have an effect on merge sorts, it might turn some otherwise stable sorts into unstable ones. Note that with lessOrEqual, you will never hit the else return 0 branch of comparator.

The ECMAScript specifications don't specify a sort algorithm, nor do they specify that it must be stable, but most of them do supply sorts that are stable given a reasonable comparator. If we extend your lessOrEquals like this:

 function lessOrEqualAge(x, y) {
     return x.age <= y.age;
 }

and try to use it to sort the following list using timsort, one of the most well-respected (and usually stable) sorting algorithms around:

 var kids = [
     {name: 'Abby', age: 7},
     {name: 'Bob', age: 7},
     {name: 'Cathy', age: 5},
     {name: 'David';, age: 4},
     {name: 'Ellie', age: 4},
     {name: 'Fred';, age: 3}
 ];
 
 clone(kids).sort(comparator(lessOrEqualAge));

You will get this:

 [Fred(3), Ellie(4), David(4), Cathy(5), Bob(7), Abby(7)]

showing that this is unstable. If you had used a simpler comparator:

 clone(kids).sort(comparator(function(x, y) {return x.age - y.age;}));

You would have gotten the arguably more correct:

  [Fred(3), David(4), Ellie(4), Cathy(5), Abby(7), Bob(7)]

which shows that it's not the comparator function, just the predicate which is causing the instability in the sort.

pp.15-18: "Data as Abstraction". In many ways, I believe this is the key insight in the entire book; it might have been better to lead off with it and develop the arguments around it.

p.19, ¶1: Why "navigating around"? This sounds out of context.

pp.24-25 "The Case for Underscore". There is also a real case to be made against Underscore, some of which you do touch on later. But I think you shouldn't simply sing it's praises without also noticing its issues, especially the parameter order that makes it much more unpleasant to curry and the awkward chain mechanism that is used where functional programmers would prefer to compose functions.

p.25: You say, "It would be fun to re-implement Underscore's capabilities from scratch, but it would serve neither myself (or my employer) nor you to do so." I couldn't disagree more. In attempting to build your own version of those capabilities, one can:

  • Learn more about the ideas and their possible implementations
  • Make improvements on the APIs presented by something like Underscore
  • Possibly create a competitor to Underscore which helps to improve the entire ecosystem.

p.25, Summary: First chapters are often a dumping ground to cover stuff that is not that interesting, used to set the stage, but not to instigate the action. I'm hoping that's what's happening here, because this chapter seemed fairly empty of interesting content.

Chapter 2

p.28: "A number can be whatever and so can a function". This is a very nice treatment of first-class, perhaps the best I've ever seen. No jargon, no obscurity, just sensible examples. Very nice! (One nit to pick: The very first example should have included var fortytwo = 42;.)

p.32, footnote3: There are so many ways one can go with this. How about, "You can't fool me; it's prototypes all the way down!" Or "... therefore, logically, there must have been a First Constructor." I love the little bits of humor throughout the book!

p.32:

  bfunc = function() { return this; };
  var b = {name: 'b', fun: bFunc};
  
  b.func();
  //=> some global object, probably Window

This is wrong. Horribly wrong. "Keep this man away from small children and junior developer" wrong. It makes one question the author's Javascript chops, and makes one wonder what other code was not actually tested before being presented in the book.

p.33: "Metaprogramming". I'm not sure what makes this so "meta". It takes advantage of a normal feature of the language already discussed that functions are first-class and hence can be used in places other than where defined and by means other than intended. This is not much different from the behavior discussed on the previous page.

p.44: Your project function is very nice, but it's a good example to use to demonstrate the isues with the Underscore parameter order. In the library I have under development, I can naively write the equivalent of your

 function project(table, keys) {
     return _.map(table, function(obj) {
         return _.pick.apply(null, construct(obj, keys));
     });
 };

as

 var project = curry(function(keys, table) {
     return map(pick(keys), table);
 });

But using a slight extension to my curry function, called useWith, I can do this in a points-free manner:

 var project = useWith(map, pick);

It's not just that the code is cleaner; it also builds in automatic currying, so that I can do:

 var getEditionResults = project(['title', 'isbn']);
 var editionResults1 = getEditionResults(library1);
 var allEditionResults = map(getEditionResults, allLibraries);

The parameter order of Underscore makes this signficantly more difficult to achieve cleanly.

p.47, Summary: Except for the horrible blunder noted on page 32, this chapter serves as a nice introduction to functional programming techniques. This chapter bodes well for the rest of the book.

Chapter 3

p.51: "Lexical scope refers to the visibility of a variable and its value analagous to its textual representation." I believe I have a good understanding of lexical scope, but I can make no sense of this sentence. Although the subsequent example eventually makes it clear what you're talking about, the description is confusing. It's also a bit misleading. If the same function were built from an Abstract Syntax Tree, not a text file, there would still be equivalent lexical scoping rules.

p.52: Unless you intend to build up a meta-circular evaluator, and unless you make this intention clear, reimplementing such fundamentals as scope binders is just noise. It does not teach anything about how to use functional capabilities, how to implement functional APIs, or how Javascript differes from other functional languages.

pp.49-58: This entire chunk seems of little use. Clearly you need to make the point of global and function scope. The rest could be replaced with a quick gloss on call stacks.

p.59, note: This typographic convention can have a serious complication. People do not only read a book from front to back and hence have this note in mind. The convention will also affect those who randomly access source code examples.

p.59, ¶1: Again, I love the humor throughout the book. I've got to note, though, that the whole Monad thing has driven closures down to a mere 18% (23% with HackerNews commentary) You've got to keep up with the trends!

p.62, footnote 9: Brilliant! Is this original?

p.63: The name averageDamp seems qute unmotivated. Is it related to a function that calculates some sort of dampening effect on a vibration? Examples probably shouldn't draw this sort of attention to themselves. They should either be extremely obvious or well explained.

p.67, Summary: I see no need for this chapter at all. Clearly you do need to discuss scoping. But this chapter seems as much as anything to be an attempt to re-implement Javascript scoping inside Javascript. I see absolutely no need for this. It seems to be showing off, and is of little benefit to the reader, either the Javascript person trying to learn more functional techniques or the functional programmer trying to see how Javascript might fit into her world.

Chapter 4

p.69: It's quite unclear if there is supposed to be and "and" or an "or" between the bullets of the definition of higher-order function. Since there are clearly higher-order functions that only match one of the last two bullets, I'm assuming you mean "or". But the first bullet seems odd. Being first-class clearly does not imply higher-order. And it's easy enough to imagine some non-first-class functions that would still be higher order: What if Java's reflection package had a "maybe" method that accepted a method and returned a new one that wrapped null-checking around it. This would definitely be higher-order, even if this were simply a method of the Method class, and not a first-class function. It would be still easier to do in Javascript, but the point is that the "higher-order" simply does not rest on "first-class".

p.69, last sentence: "It bears repeating that the capitalization of captured variables is not a recommended practice, but it serves well for book writing". It also bears repeating that it does not serve at all well for book reading.

p.74:

 iterateUntil(function(n) { return n+n },
              function(n) { return n <= 1024 },
              1);

This seems to presuppose a known answser. The example would be much better with n <= 1000:

 iterateUntil(function(n) { return n+n },
              function(n) { return n <= 1000 },
              1);
 //=> [2, 4, 8, 16, 32, 64, 128, 256, 512]

p.76: I like invoker a lot and will definitely adopt it for my own library. Very nice!

p.77: "Capturing Variables for Great Good". I'm assuming that this title is a bit of homage to Miran Lipovača's Learn You a Haskell for Great Good, but without any further obvious clues, this is fairly obscure.

p.77-79: What is the point of syntactic complexity of

 return [prefix, counter++].join('');

versus the clearly simpler version

  return prefix + (counter++);

Also, stylistically, this section seems to go against most of the book in which examples gradually build to the best solution. Why is the favored solution tucked in the middle in this case? It seems at best untidy.

p.80: fnull is a nice little function. It was the very first snippet I shared with co-workers.

p.82, ¶2: "... the flexibility provided by functions that take return other functions cannot be understated." I believe you mean "overstated". I hope you mean "overstated".

p.82, last ¶: "to avoid the dreaded pattern:

 {
   errs.push(check.message);
   return errs;
 }

." This is not motivated at all. Why is this dreaded? The, "We'll talk about this in Chapter 7," is definitely not enough motivation.

p.83, ¶1: Why "psuedo-metadata"? It seems to fit most of the criteria I've ever used for metadata. It this just because it's not specifically called out as such in the language specs?

p.83: Your validator API seems much more awkward than one that does either

 return {passes: true};

or

 return {
   passes: false,
   message: 'a failure in life'
 };

What advantages does yours have over this simpler one?

p.84:

 function aMap(obj) {
   return _.isObject(obj);
 }	   

This is quite confusing in a Javascript book. It makes one think you're talking about cartography or the first step in a map-reduce algorithm. While some will recognize the HashMap reference, it's out of place here.

p.85, Summary: While I still disagree that higher-order functions are necessarily first-class, this is much better stated here than at the beginning of the chapter, where, first-class, accepting function parameters, and returning function results were listed at the same level. Overall, this chapter presented some great material, and seems to serve as a great basis for building up a functional mind-set in the reader.

Chapter 5

p.87: In the dispatch function, I can make no sense out of splitting arguments into target and args only to recombine them on every iteration, without otherwise using either of them. Huh?

p.91: "To extend the performCommandHardCoded function, you would need to go in and changed [sic] the actual switch statement itself." No. You could perform exactly the same type of wrapping as done in the technique you present:

 var performAdminCommandHardCoded = function(obj) {
	if (obj.type == "kill") {
	    return shutdown(obj.hostname);
    }
	return performCommand(obj);
 };

This is slightly more verbose, but it's not as intrusive as you make it sound.

pp92-93: This is a confused and misleading introduction to currying. That invoker returns a function which is then applied means that it's a higher-order function. It is no more curried than createScaleFunction (p.61) This might imply currying:

 invoker('reverse')([].reverse)([1, 2, 3]); //=> [3, 2, 1];

p.94, "To Curry Right, or To Curry Left": This awkward curry-right notion seems an unfortunate consequence of the use of Underscore. Most of the owrld curries from the left. Damn British drivers!

Also, illustrating with one of the very few examples (division) where right-curry actually seems more natural than left-curry feels like an attempt to bury the awkwardness. Yes, this is cleaner:

 var divideBy10 = rightCurry(div)(10);

than

 var divideBy10 = leftCurry(flip(div))(10);

But such examples are a small minority.

p.96, ¶3: "... but I like to be explicit when currying." You should probably make it clear that this is an unusual stance.

p.98, songsPlayed example: You take it for granted that the reader knows off-had the signature of Underscore's uniq function. I, for one, have no idea what your false is for.

p.100, "The Disadvantages of Currying in JavaScript": While I do think that dynamic function signatures are one of the strengths of Javascript, I believe this section could better be titled, "The Mismatch between Functional Programming and Dynamic Function Signatures." The more I do with functional programming, the less I find use for dynamic signatures, and the less helpful I find them. I believe this is more a mismatch of features than a flaw of currying.

p.101: There's an alternative way of looking at the difference between curry and lPartial: lPartial only allows you a single opportunity to specialize your function. Once it's configured, you'd have to call lPartial again to make it further configurable. curry doesn't share this issue. For Javascript, I believe you can get the best of both worlds. In the library I'm building, I expose this version of curry and apply it to pretty much all exposed functions of more than one parameter:

 var curry = function(fn) {
     var arity = fn.length;
     var f = function(args) {
         return function () {
             var newArgs = (args || []).concat(slice(arguments, 0));
             if (newArgs.length >= arity) {
                 return fn.apply(this, newArgs);
             }
             else {return f(newArgs);}
         };
     };
     return f([]);
 };

Braithwaite's Allongé has something similar, if I recall correctly. Technically, this is more than just what some languages call curry, as any of the functions along the way allow you to pass more than a single parameter, but I have little hesitation using the name "curry" for it, as it can be used exactly as you would a true curry if you want to pass only single arguments. (This function still needs a little work. I really want to make the intermediate functions have the correct arity. My first attempt failed, though, and I haven't gotten back to it; it's far from the top priority at the moment.)

p.101, figure 5-4: I'm not a particularly visual person, so I often gloss over these. But this combination of diagram and caption is quite confusing. Why is it "FUNC" and "A" in one, "foo" and "42" in the next; "curried" and "1" in one bit of the caption, "partially" without a parameter in another? WTF?

p.103, last example, last two ¶s, and footnote 10: It is this sort of behavior that makes it pretty clear to me that varargs and curry/partial application simply shouldn't be mixed. But you can define the function I mentioned above and still easily get a great deal of flexibility.

pp.105-07: This is a very interesting approach. Is this mainly theoretical, or do you find yourself using it in practice?

pp.108-09, "Stitching Functions...": This seems as though it would have been a good opporunity to mention points-free style or at least the general idea of building up functions before applying them to data.

p.110, Summary: I had a difficult time with this chapter. I thought it was well-written, and presented some interesting material. But it was also the place where I felt most acutely that you'd hitched yourself to the wrong horse with Underscore. So throughout I felt as though the code could be so much better, if only...

Chapter 6

p.113: This could use a better example than:

 unzip(_.zip([1, 2, 3], [4, 5, 6])); //=> [[1, 2, 3], [4, 5, 6]]

It's somewhat unclear that this function is actually doing anything at all, but if it is, the intermediate:

 [[1, 4], [2, 5], [3, 6]]

is unmotivated, essentially meaningless.

p.118: You might want to define "graph". I have a background in combinatorics, and "graph" is second nature, but I'm not sure how many readers might not think you're talking about Cartesian line plots...

p.122-23: Are these functions really useful on arguments rather than on arrays? These are clearly good for single arguments or for arrays, but how often would you expect them to be used for multiple arguments? I might, for instance, do this:

 var evenNbr = and(isNbr, isEven);
 evenNbr(3); //=> false
 evenNbr(108); //=> true
 var allEvenNbrs = all(evenNbr);
 allEvenNbrs([2, 4, 6, 108]); //=> true	 

I wouldn't expect to need it, but if I really needed to run them on arguments, I could then simply use:

 var allArgsEvenNbrs = unsplat(allEvenNbrs);
 allArgsEvenNbrs(2, 4, 7, 108); //=> false

(And while I've brought up the number 108, I noticed it throughout your book alongside 42 and was curious about it. Of course all programmers recognize 42 from Douglas Adams. Do you use 108 because it's 1^1 * 2^2 * 3^3? That was the only particularly noteworthy feature I could find about it. Extending that to 4 is 27648, which is not particularly interesting, but I was wondering if you'd ever gone the next step to notice that 1^1 * 2^2 * 3^3 * 4^4 * 5^5 = 86400000, which is exactly the number of milliseconds in a day? Just one of those nice coincidences!)

p.124: I love "codependent functions"!

p.118-29: It's very difficult to keep in mind as we get further away from it that your representation of influences does not capture the tree structure of the diagram intact. I keep looking for a version of the visitor pattern that fed "Smalltalk" would return ["Self", "JavaScript", "Lua"].

p.129: Only on my third reading did "evenOline" and "oddOline" resonate with "trampoline" Maybe I'm just dense, but I actually have been thinking a lot recently about recursion and trampolining.

p.131-34: Perhaps it was just the timing of the book, but I was disappointed that there are no mentions of libraries like dtao's Lazy.js and goatslacker's lz.js.

p.136-37: There are really good reasons to avoid deep recursion in Javascript, especially performance problems due to the lack of tail-call optimization. And I agree that it is better to reuse existing solutions rather than reinventing everything. But I absoluterly disagree with this: "recursion should be seen as a low-level operation and avoided if at all possible" What kind of functional programming practices are you trying to instill?

p137, Summary: First of all, thank you for not doing factorial or Fibonacci! But I do feel that your examples of recursion were not ones that showed it off well, as though you were coming at it from the perspective of trying from the beginning to discourage readers from using recursion by not pointing out its strengths. I'm pretty certain that's not your intent, but it does seem to be a result of the examples chosen. That's disappointing.

Chapter 7

p.142: It might be useful to discuss a function like

 var composeRandomString2 = function(len, charGen) {
     return repeatedly(len, charGen || generateRandomCharacter);
 };

which I have not tested, but which looks to have the same kind of testability as your function and a simpler API for regular use.

*p.146, "Purity and the Relation to Idempotence": There are many different notions of idempotence in mathematics and computer science. The one presented here is fine in mathematics and possibly for some really purely functional languages, but it's meaningless in Javascript.

 var f = function(x) {x.val = x.val + 1; return x;};
 var x = {val: 0};
 // {val: 2} ~ f(f(x)) === f(x) ~ {val: 1}

By this definition, even as destructive an operation as f is idempotent.

I don't have a good formal description for a useful notion of idempotence, but it would have to involve the global state of the JS environment and an operation that changed the state of that environment, such as an assignment as the result of a function call as well as its side-effects, something to the effect that

If for any Global State, S,

 op: S -> S'

implies that

 op: S' -> S';

Then op is idempotent. (A little hand-waving here over mathematical formalities of what constitutes global states and operations, but I think the ideas are clear.)

I think this section just ends up a distraction in the text, as the definition adds nothing to the discourse, and the rationale is not really motivated.

p.150, "Immutability and the Relationship to Recursion": This point is very important, and should have somehow been better paired with the "Low-level Operation" section on pages 136-37. Although it does so in an impure manner interally, Javascript can be very much a functional language at the function boundaries.

p.153: "[F]reezing arbitrary objects might introduce subtle bugs when interacting with third-party APIs. Your options are therefore limited to the following: ..." Why is there less of a chance of subtle bugs with any of these? An API that depends on permanent mutation of objects it interacts with will fail in any of these scenarios. It is only ones that use soome sort of temporary marker mutation that I could see failing with freeze but working with code that otherwise expects immutability. Am I missing something?

pp.155-56: I'm still trying to decide what I think of a theoretically immutable API linked with public mutable fields distinguished only by a naming conventions. I'm definitely not thrilled with it, although I'm not a big fan of get/set interfaces either.

pp.160-162: Immediately after discussing the advantages of hiding constructor implementations (pp.159-60), you present this example as a Container constructor and prototype? This is pretty inconsistent!

p.163, Summary: This chapter felt more argumentative than the others; as though you were trying to prove a point to to an expected critic. Perhaps you were right to do so, because I was less convinced by this material than I was by that in several earlier ones or in the next one. And it's not that I don't believe that functional purity and immutability unimportant; it's simply that I feel as though you didn't make the case for them, often delving into irrelevancies.

Chapter 8

p.167, ¶2: "With the use of _.result" should probably read, "With the use of _.value", correct?

pp.165-68: Doesn't chain feel at odds with the way functional programming tends to proceed? Why not:

 compose(sort, tap(myLogFunc), pluck('title'))(library);

or

 pipe(pluck('title'), tap(myLogFunc), sort)(library);

p.185: Maybe it's just me, but it seems that the notion that you're presenting monads, even if you find them somewhat anemic ones, deserves more than just a a footnote.

p.186-187: Using _ (the underscore character) for pattern matching in negativeSqrAction and negativeSqrAction2, as might be done in other FP languages, really doesn't work well here. First of all, it's not a familiar paradigm to Javascript programmers. But more importantly, with the ubiquity of the Underscore library throughout the book, this can lead to a good deal of confusion.

p.189, Summary: Overall, I find this not only the best written chapter in the book, but also the most motivating and innovative code presented. I'm not sure how quickly I'd adopt the techniques presented, but I would certainly consider them.

Chapter 9

p.194, ¶2: As noted earlier, I love the humor sprinkled throughout. I really like, "thus blowing their abstraction budget."

p.202, ¶2: "Using a stripped-down JavaScript class library based on a tiny library created by John Resig..." If it's that small, show us the code. If not, this is a distraction; just use the built-in prototype capabilities.

p.206: "The mixin protocol for Container is as follows... Extension Protocol ... Interface Protocol ..." Huh, what?

This is not just me playing dumb or trying to put myself in the shoes of a less experienced developer (which I'm more than willing to do in the course of a book review.) Even on the second time through this, I have no idea what you mean by "the mixin protocol for Container."

Is this the API that Container will expose when it is turned into a mixin? Or is it the API that any oject which wants to be mixed in with a particular Container (or the class of Container?) would need to expose?

I don't know if this is your own terminology or a standard one that you expect readers to recognize, but it has me stumped.

p.207: At least the "extension protocol" is starting to make more sense.

p.209: "The use of the JavaScript closure mojo ... is the common way to hide data, and is therefor the preferred way to hide a bit of mixin state as well." Watch it here; you're on thin ice, justifying this part of your uncommon functional techniques on how common their imperative counterparts are. If we're basing things on how common the techniqes are, we won't likely move beyond imperative constructs.

p.209, footnote 5: In other words, you're an optimist!

p.214, footnote 6: I probably will not be the only reader who doesn't know Scala nor recognize the Cake pattern (although I now realize that Braithwaite was also referring to it in Allongé.)

p.216, ¶1: "I believe that by putting a functional face on the container types, I've achieved a level of flexibility not obtainable via an object/method focus." I don't see that you've demonstrated that. You've really only shown that you can replace specific method calls with specific functions calls. How is that more general? It's not that I don't like the functional interface, but how do you support the claim for additional flexibility?

p.216, Summary: This chapter needs much more why. It has plenty of how, but leaves the book on a pretty weak note.

End Material

As the code examples were not self-contained, but referred back to functions defined earlier, it would be very useful to have an index of functions noting the page on which they're defined. Even better would be if it described function signatures and a brief description of what they did, but at least an index would be very helpful.

pp.222-23: There are a few other libraries that I think should probably deserve a mention here:

  • Lazy.js is a Lazy list implementation
  • Lz is another
  • Fantasy Land is a JavaScript spec for Algebraic Data Types, and it's tracking the progress of several implementations.

Conclusion

This is a good book, but not the great book I was hoping for.

Possibly I was looking for a book targeted a bit differently. I really would like a book targeted squarely at Javascript developers, teaching them functional programming techniques. This book can't quite seem to make up its mind whether it wants to be that sort of book or whether it wants to the book to show the functional programming world that Javascript is a language closer to them than they knew. While the latter might be a worthwhile goal, it is not a help to me as a Javascript developer.

Chapter 3 seems especially egregious from this perspective, spending much of it's time showing how, from within the language, you might implement the core language scoping constructs. That can be quite fun to do, but should presumably be saved for part of a metacircular evaluator implementation, which would likely be done only after you'd mastered all the basics.

Michael Fogus is at his best when talking about specific functions and the patterns in which to apply them. The chapters richest in these -- Chapters 2, 4, 5, and 8 -- were the best ones in the book. I found it unfortunate that he shied away from using "Monad" for the construct he creates in Chapter 8, but that was some of the most intereting and useful material in the book. While other chapters also have some of this interesting material, they can be somewhat marred by unpersuasive arguments Fogus makes about programming style.

The choice to build on top of Underscore is quite problematic. While Underscore offers some functional techniques, it does not do so in the most helpful manner, and code that builds upon it is often less clean than it might otherwise be. This seems a problem that Fogus' text can never escape, even when he occasionally notes the difficulties.

I don't want to leave the impression that the book was terrible. In fact there are inspired moments in the book. There are wonderful sections, some very interesting functions. The flow-based programming material alone makes the book worth reading. And Fogus blends a good deal of humor throughout the book, sometimes obviously, occasionally subtly. It's quite pleasant to read.

But I'm still waiting for the book that will knock the socks off everyone and demonstrate that functional programming is the future -- even a major part of the future -- of Javascript. Functional Javascript was not it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment