Pragmatic, balanced FP in JavaScript. @FLJSBook on twitter.

Overview

Functional-Light JavaScript

License: CC BY-NC-ND 4.0

Book Cover

This book is a balanced, pragmatic look at FP in JavaScript. The first edition is now complete. Read here online for free, or:

Buy on Leanpub Buy on Manning Buy on Amazon

"Functional-Light JavaScript" explores the core principles of functional programming (FP) as they are applied to JavaScript. But what makes this book different is that we approach these principles without drowning in all the heavy terminology. We look at a subset of FP foundational concepts that I call "Functional-Light Programming" (FLP) and apply it to JavaScript.

Note: Despite the word "Light" in the title, I do not consider or recommend this book as a "beginner", "easy", or "intro" book on the topic. This book is rigorous and full of gritty detail; it expects a solid foundation of JS knowledge before diving in. "Light" means limited in scope; instead of being more broad, this book goes much deeper into each topic than you typically find in other FP-JavaScript books.

Let's face it: unless you're already a member of the FP cool kids club (I'm not!), a statement like, "a monad is just a monoid in the category of endofunctors", just doesn't mean anything useful to us.

That's not to say the terms are meaningless or that FPrs are bad for using them. Once you graduate from Functional-Light, you'll maybe/hopefully want to study FP more formally, and you'll certainly have plenty of exposure to what they mean and why.

But I want you to be able to apply some of the fundamentals of FP to your JavaScript now, because I believe it will help you write better, more reasonable code.

To read more about the motivations and perspective behind this book, check out the Preface.

Book

Table of Contents

Publishing

This book has been published and is now available for purchase (in both ebook and print formats) from these sources:

Buy on Leanpub Buy on Manning Buy on Amazon

If you'd like additionally to contribute financially towards the effort (or any of my other OSS work) aside from purchasing the book, I do have a patreon that I would always appreciate your generosity towards.

patreon.png

In-person Training

The content for this book derives heavily from a training workshop I teach professionally (in both public and private-corporate workshop format) of the same name.

If you like this content and would like to contact me regarding conducting training on this, or other various JS/HTML5/Node.js topics, please reach out to me through email: getify @ gmail

Online Video Training

I also have several JS training courses available in on-demand video format. I teach courses through Frontend Masters, like my Functional-Light JavaScript v2 workshop. Some of my courses are also available on PluralSight.

Contributions

Any contributions you make to this effort are of course greatly appreciated.

But PLEASE read the Contributions Guidelines carefully before submitting a PR.

License & Copyright

The materials herein are all (c) 2016-2018 Kyle Simpson.

Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 4.0 Unported License.

Comments
  • Some issue I encountered

    Some issue I encountered

    Hi, Kyle

    While I updating the Chinese translation, I encountered some small problem. Perhaps some editorial issue:

    1. ch3.md line 149:

    Warning: Although the () => p2 arrow function version is shorter than constant(p2), I would encourage you to resist the temptation to use it. The arrow function is returning a value from outside of itself, which is a bit worse from the FP perspective. We'll cover the pitfalls of such actions later in the book (see Chapter 5, "Reducing Side Effects").

    At the last of this paragraph, I think there should be a link to the actual location, just like other place where cross reference happened.

    1. ch3.md line 520:

    The three sets of (..)s denote three chained function calls. But perhaps splitting out each of the three calls helps see what's going on better:

    "The three sets of (..)" -- I didn't spot where are they. Maybe you planned to write the previous code snippet like:

    curriedAjax(..)(..)(..);
    

    and then changed your mind?

    I will keep posting the issue I found.

    opened by JoeHetfield 23
  • Chapter 2 equation - f(x) = 2x^2 + 3

    Chapter 2 equation - f(x) = 2x^2 + 3

    Yes, I promise I've read the Contributions Guidelines.

    I found this equation confusing, so I wonder if it needs an explanation. I'm actually not familiar with the symbol ^ - I've looked it up and it seems to be exponent, which I take as "to the power of". Yet, when I try it in the console (so based on JavaScript), it seems to act like a plus. So for 8^2 , I'd expect 64, but in the console, the answer is 10.

    Anyway, the point is you've used an equation early on that's already flummoxed me and stopped me continuing, which is ironical given what the book is trying to do.

    NB I don't have a strong programming background, but I did have a fairly strong mathematical education - although that was over 20 years ago and largely forgotten!

    opened by yodiyo 16
  • Testimonials

    Testimonials

    If you've read at least several chapters of this book, and liked it so far, I need your help!

    I need a few quotes/testimonials. What I need is a 1-3 sentence (<150 words) quote from you about the book/topic, why you have found it useful, any positive impacts it's had on your coding, etc. I also need your name/job-title as you'd like to be attributed.

    By replying to this thread, you are agreeing to let me use some or all of what you submit in marketing for the book, including but not limited to, the book cover, this repository, the twitter account, any future book website, etc.

    help wanted editorial 
    opened by getify 14
  • Chinese translation complete

    Chinese translation complete

    Hi, Kyle.

    I'm glad to tell you that the Chinese translation of the main text of Functional-Light-JS had complete:

    This is the repo

    Just like YDKJS, it will be a Chinese mirror of your master branch.

    The translation of appendix (a little difficult to me, I need more time to study on it) and preface still in progress, hopefully finished within next month.

    Please tell me what you think.

    foreign language translations 
    opened by JoeHetfield 12
  • A compose() with reduce() and multiple args

    A compose() with reduce() and multiple args

    I understand that your chapter about Alternate Implementations for compose() is a way to make your reader think about the concept.

    So I guess it's a success: it stroke me as obvious that there is another (maybe simpler) way to make the compose() with reduce() accept multiple args: by handling the first call with the given arguments then subsequent calls with reduce():

    function compose2(...fns) {
        return function composed(...result){
            var first = fns.pop()(...result);
            return fns.reverse().reduce( function reducer(result,fn){
                return fn( result );
            }, first );
        };
    }
    

    Though I'm quite proud of myself, I'm not quite sure it was a matter for an issue :) I'll let you judge.

    opened by solendil 12
  • Chapter 7: How are closures and objects isomorphic?

    Chapter 7: How are closures and objects isomorphic?

    Closures and objects differ in how they allow access. Is information access not a behaviour? If it is, by definition doesn't that mean they are not isomorphic?

    opened by jonathanstiansen 8
  • What about a program the only does computation?

    What about a program the only does computation?

    In chapter 5 you say:

    The punchline to this chapter: it's impossible to write a program with no side effects. Well, not impossible; you certainly can. But that program won't do anything useful or observable. If you wrote a program with zero side effects, you wouldn't be able to tell the difference between it and a deleted or empty program.

    I think that a good example of a computer program with no side-effects is the one of a program that only does computation.

    opened by adriano-di-giovanni 8
  • ES6 curry function in ch3

    ES6 curry function in ch3

    I am looking over the function and it seems odd. Is the line (nextCurried = prevArgs => performing an implied variable definition without var|let|const ? And if so, would that then be hoisted to the parent scope?

    opened by rkichenama 8
  • Ch3 - questioning readability of point-free style

    Ch3 - questioning readability of point-free style

    Hello, I'm following along, and, as I look at this snippet:

    var printIf = reverseArgs(
        uncurry( partialRight( when, output ) )
    );
    

    I start to wonder whether FP helps or not in writing more readable code. Is it just me or that's a hard to follow line? Is it just a matter of getting used to FP style and will sound obvious to me as my FP skills develop? That looks to me like something I would need to explain to some fellow team member, if I'll ever write anything like it!

    What are your thoughts on this? Honestly, that piece of code alone is frightening me a bit, but maybe that's already one of those cases where you suggest that we stop and reconsider point(-ful?) style

    opened by mendaomn 8
  • Tech Edit: Chapter 3

    Tech Edit: Chapter 3

    Loved this chapter. I feel it really gets the reader excited about this stuff!

    What follows is a collection of mostly unnecessary ideas rather than "tech edits". I thought the tech was spot on and the examples were great so not much to do there. Either way, here are some thoughts:

    1. You demonstrate equational reasoning several times both explicitly and implicitly.
    • Inlined the partiallyApplied definitions
    • Refactorings like mappers and constant (and several others)
    • ExplainingprintIf refactor.

    I wonder if it's worth a mention that FP goes out of its way to preserve equational reasoning. Not sure if you'd want that, just saying something since these are great examples of it in action.

    If it is a thing you'd want to consider, maybe also make it explicit each time, formally, instead of using english to explain refactorings:

    p1.then( foo ).then( () => p2 ).then( bar ); // original
    ==
    p1.then( foo ).then( constant( p2 ) ).then( bar ); // constant
    == 
    p1.then( foo ).then( ( v => () => v )( p2 ) ).then( bar ) // inline constant
    ==
    p1.then( foo ).then( () => p2 ).then( bar ); // evaluate constant application to recover original
    

    Anyways, just a thought. I understand this is supposed to be an informal book so no worries if this is too mathy - just something to consider so the reader gets a glimmer of the whole "reasoning about" thing and sees why purity can be so important later on.

    1. That _ in p1.then( foo ).then( _=>p2 ).then( bar ); may confuse some

    I get the "why the underscore argument?" question a lot. Maybe a note that _ is often used to mean "ignored parameter" in FP would be good or just move to () => p2.

    1. "The advantage of currying here is being able to do something like curriedSum(1)(2)(3), which returns a function".

    This line didn't quite drive home the distinction between partial and curry for me. I see the main difference/advantage is that currying is used at definition time and partialApplication is used "on the fly" by the caller. I suppose that's implied by the examples, but might be worthwhile to spell out since they are so similar.

    1. "Don't just assume that uncurry(curry(f)) has the same behavior as f".

    This is true for some libs, but lots do strive to make that isomorphism / adjunction (an iso between homSets) work, which then gives rise to the reader/writer monads (and their respective comonads). In any case, I'd mention it's false for this implementation only since it is true for some other libs.

    1. Should it say something to the effect of "When currying, it's often useful to analyze argument position and put the "data" last". And "Usually one curries everywhere in this style". These techniques combined usually sidesteps the need for uncurry, partialRight, and reverseArgs".

    I absolutely love how the printIf example ties in everything from the chapter. It is built up beautifully from the prior concepts. Although, having written code like this for a while, I found myself asking "why haven't I encountered the reverse/partialRight/uncurry pattern very often?"

    I think it's two things:

    • When currying is on by default, one rarely needs to uncurry or partial
    • Argument order is usually defined for data to be last, therefore known args are at our finger tips and partialRight is almost never necessary. On rare occasions, like the when example, flip() usually does the trick.

    So the common result in a ramda or haskell app would be something like: var printIf = flip( when )( output );

    With the full code being:

    const when = R.curry(function when(predicate, fn, args) {
      if (predicate( args )) {
        return fn( args );
      }
    });
    
    function output(msg) {
      console.log( msg );
    }
    
    function isShortEnough(str) {
      return str.length <= 5;
    }
    
    var isLongEnough = not( isShortEnough );
    
    var printIf = R.flip( when )( output );
    
    var msg1 = "Hello";
    var msg2 = msg1 + " World";
    
    var printShort = printIf( isShortEnough );
    var printLong = printIf( isLongEnough );
    
    printShort( msg1 ); // Hello
    printShort( msg2 );
    
    printLong( msg1 );
    printLong( msg2 ); // Hello World
    

    The tools here are worthwhile and valid, I just don't end up encountering the situation much in day-to-day code for the reasons stated above.

    I'm not sure what to do with all of these thoughts and ideas, but I'm happy to have shared them. Either way if you decide to adopt them or not, I won't have emotions about it; just wanted to share my brain dump after reading.

    Again, great chapter!

    editorial 
    opened by DrBoolean 6
  • Sign up for Editing assistance

    Sign up for Editing assistance

    Filing an issue to to track those who would like to volunteer to assist with editing. Kyle works very hard on these books and contributes volumes to the community, let's give him a hand!

    If you'd like to help, this is the place to make that fact known!

    editorial 
    opened by mindpivot 6
  • Incorrect description of flatMap (Appendix B)

    Incorrect description of flatMap (Appendix B)

    Yes, I promise I've read the Contributions Guidelines (please feel free to remove this line -- if you leave this line here, I'm going to assume you didn't actually read it).

    I think Appendix B incorrectly describes the flatMap (chain, bind) monad method. From the description, this method is used to leave the monad (as an example, the identity function was used to return a value without a "wrapper"). However, the monad does not actually expose any method for doing this.

    The flatMap method is used to combine subsequent monads together in a sequential manner, which is the main strength of monads.

    The flatMap function signature could look like this (A is a type of current value, F is the monad itself):

    function flatMap<B>(fn: (value: A) => F<B>)
    

    Therefore, the identity function cannot be used inside flatMap. In my opinion, the following is a valid example of using flatMap:

    
    const a = Just.pure(5)
    const fn = (value) => Just.pure((value + 3).toString())
    
    const b = a.flatMap(fn) // result: Just("8")
    
    opened by PawelJ-PL 7
  • Chapter 4: Abstraction Example wrong logic

    Chapter 4: Abstraction Example wrong logic

    In the last abstraction example (when you took the abstraction too far) the logic in the code is wrong compared with the original example shown earlier. So if this is intentionally, please disregard the comment below and just add clarification in the description of the example.

    1. First mistake

      function isPropUndefined(val,obj,prop) {
          return isUndefined( obj[prop] );
      }
      
    • The code above is ok to check if a property of an object is undefined, but here you change the logic of the previous example and check if a property of the store is undefined.

      • Original logic:

        if (evt.name !== undefined) {
              storeData( events, evt.name, evt );
        }
        
    1. Second mistake
    • You return isUndefined result which is true if the property is undefined, so the logic is wrong once again.

    Proposal:

    • Just change the name of the function and wrap the function in another one which accepts the property to be checked against undefined. This will lead to the need of invoking the function with an argument name in this case.

      function isPropDefined(prop) {
          return function (obj) {
              return !isUndefined(obj[prop]);
          }
      }
      
      function trackEvent(evt) {
          conditionallyStoreData(events, evt.name, evt, isPropDefined('name'));
      }
      
    • Now we could change the invokation of checkFn to be with only one argument.

      if (checkFn(value)) {
          store[location] = value;
      }
      
    second edition 
    opened by vesheff 5
  • Chapter 9: not getting the standalone implementation reducer function

    Chapter 9: not getting the standalone implementation reducer function

    Quote:

    But a standalone implementation of reduce(..) might look like this:

    function reduce(reducerFn,initialValue,arr) {
        var acc, startIdx;
    
        if (arguments.length == 3) {
            acc = initialValue;
            startIdx = 0;
        }
        else if (arr.length > 0) {
            acc = arr[0];
            startIdx = 1;
        }
        else {
            throw new Error( "Must provide at least one value." );
        }
    
        for (let idx = startIdx; idx < arr.length; idx++) {
            acc = reducerFn( acc, arr[idx], idx, arr );
        }
    
        return acc;
    }
    

    Question:

    Perhaps I don't understand the context, but how to call this function without passing an initialValue?

    opened by qianist 1
  • Fix variable name

    Fix variable name

    Changed variable name in explanations (total) to match num1 in code snippet

    Yes, I promise I've read the Contributions Guidelines (please feel free to remove this line -- if you leave this line here, I'm going to assume you didn't actually read it).

    second edition 
    opened by amiralies 1
Owner
Kyle Simpson
I like to explore JS and FP techniques. Helping build a culture of engineering excellence for my employer.
Kyle Simpson
Fuck Twitter NFTs - Userscript to delete or block all occurances of NFT Users on Twitter

FuckTwitterNFTs Fuck Twitter NFTs - Userscript to delete or block all occurances of NFT Users on Twitter Userscript will by default, attempt to delete

Blumlaut 1 Jan 20, 2022
Twitter bot to find what song is playing in a given uploaded twitter video.

what-song-is-this Twitter bot to find what song is playing in a given uploaded twitter video. How to setup. yarn install How to run. via npm script ya

Akinwande Akinboluwarin 17 Dec 11, 2022
A Twitter filtered search to only get the live broadcasts hosted on Twitter itself, Built using Vanilla JS and Node.js

Twitter Broadcasts Search A Twitter filtered search to only get the live broadcasts hosted on Twitter itself, Built using Vanilla JS and Node.js. Live

Mohammad Mousad 2 Oct 6, 2022
A book series on JavaScript. @YDKJS on twitter.

You Don't Know JS Yet (book series) - 2nd Edition This is a series of books diving deep into the core mechanisms of the JavaScript language. This is t

Kyle Simpson 162.7k Dec 29, 2022
How to say Hello World via the Twitter API from browser-based JavaScript.

Twitter Hello World Suppose I want to write an app that runs in the browser that just says Hello World from my Twitter account. This is the canonical

Dave Winer 3 Jun 7, 2022
A Cloudflare Worker for fetching data from Twitter User API.

cloudflare-worker-twitter This is a Cloudflare Worker for fetching data from Twitter User API. ❔ About this project I created this Worker to hide my A

Arda Soytürk 12 Oct 1, 2022
The Web 3.0 social layer built on top of Twitter

Niftycase – The Web 3.0 Chrome extension for Twitter Niftycase is a open-source Chrome extension that allows you to view anybody's NFTs directly on Tw

Matt Welter 16 Jul 14, 2022
Twitter RSS (.xml) Feed Scraper Without Developer Authentication

Twitter RSS Feed Scraper Without Authentication Command-line application using Node.js that scrapes XML feeds from Nitter, the free and open source al

Jason Vu 4 Jun 15, 2022
A fresh look for the Hackage. Follow us: https://twitter.com/HackageUI

Hackage UI Fresh look for the https://hackage.haskell.org/. Work in progress. Search Search on Hoogle. Search on Hackage. Full-text search integration

visortelle 97 Dec 28, 2022
It consists of a recreation of Twitter, to put into practice both Front-end and Back-end knowledge by implementing the MERN Stack together with other technologies to add more value to the project.

Twitter-Clone_Back-end ✨ Demo. ?? About the project. ?? Descriptions. It consists of a recreation of Twitter, to put into practice knowledge of both F

Mario Quirós Luna 5 Apr 12, 2022
Twitter recommends that the majority of developers start to think about migrating to v2 of the API

Passport-Twitter2.0 with PKCE Twitter recommends that the majority of developers start to think about migrating to v2 of the API. This package is a Pa

null 11 Dec 11, 2022
This project is used to extract media from various posting platfroms like Twitter, Reddit, Pixiv, Youtube and many other

Social-Picker-API This project is used to extract media from various posting platfroms like Twitter, Reddit, Pixiv, Youtube and many others. It's writ

Serge 11 Nov 29, 2022
Uma aplicação back-end para consumo e envio de frases/mensagens semelhante ao twitter.

Tweteroo Uma aplicação back-end utilizando o nodemon para rodar o servidor e o express para consumo e envio de frases/mensagens. Rodar projeto Após cl

Vinícius 2 Feb 3, 2022
NFTBlocker for Twitter

NFTBlocker for Twitter The extension is available on both the Firefox Add-ons Store and the Chrome Web Store. This extension automatically block anyon

Théo Convolte 15 Sep 15, 2022
A Twitter bot that tweets all ERC-721 NFT sales for a given contract 🤖

NFT Sales Twitter Bot ?? This was made to succeed my Opensea Sales Twitter Bot repo; while relying on the OpenSea Events API is simpler, it doesn't in

Daniel Griffin 73 Jan 2, 2023
"Longer in Twitter" est une extension Chrome affichant les TwitLonger directement dans un tweet.

Longer in Twitter "Longer in Twitter" est une extension Chrome affichant les TwitLonger directement dans un tweet. Installation Longer in Twitter ne f

Johan le stickman 4 Sep 22, 2022
Remix Auth plugin for Twitter OAuth 1.0a

Remix Auth Twitter Remix Auth plugin for Twitter OAuth 1.0a. Supported runtimes Runtime Has Support Node.js ✅ Cloudflare ✅ Demo Try out live demo (sou

na2hiro 13 Dec 31, 2022
🐦 A Twitter clone with Remix and Kontenbase

Writter A Twitter clone with Remix and Kontenbase. Styled with Chakra UI. Features What's implemented Landing page Authentication/Authorization Sign u

Kontenbase Team 11 Jun 26, 2022
Lets you add a character to Hacker News links to add social media and OpenGraph previews for sharing on things like Slack or Twitter.

news.ycombinator1.com Lets you add a character to Hacker News links to add social media and OpenGraph previews for sharing on things like Slack or Dis

Ian Langworth ☠ 38 Sep 18, 2022