by rajaraodv

Functional Programming In JavaScript — With Practical Examples (Part 2)

In Part 1, we talked through: Functional Programming basics, Currying, Pure Functions, “Fantasy-land” specs, “Functors”, “Monads”, “Maybe Monads” and “Either Monads” via couple of examples.

In this part, we’ll cover: Applicative, curryN function and “Validation Applicative”.

Thanks to FP gurus Brian Lonsdorf, keithalexander and others for reviewing ??

Example 3— Assigning Values To Potentially Null Objects

FP Concepts Used: “Applicative”

Use Case: Let’s say we want to give discount to the user if the user is logged in and if we are running promotion (i.e. discount exists).

Let’s say we are using the applyDiscount method below. As you can imagine, applyDiscount might throw null errors if either the user (the left-hand side or the discount (the right-hand side) is null.

//Adds discount to the user object if BOTH user and discount exists.
//Throws null errors if either user or discount is nullconst applyDiscount = (user, discount) => {    let userClone = clone(user);// use some lib to make a copy  
  userClone.discount = discount.code;   return userClone;
}

Let’s see how we can solve this using “applicative”.

Applicative:

Any Class that have a method “ap” and implements the Applicative spec is called an Applicative. Applicatives can be used in functions that are dealing with null values on both left-hand-side(user) and right-hand-side(discount) of the equation.

It turns out “Maybe” Monads (and every Monads) also implement “ap” spec and hence are also “Applicatives” and not just Monads. So we can use “Maybe” Monads to deal with null at function level.

Let’s see how we can solve make applyDiscount work using Maybe used as an “applicative”.

Step 1: wrap our potential null values in Maybe Monads

const maybeUser = Maybe(user);
const maybeDiscount = Maybe(discount);

Step 2: Rewrite the function and curry it so we can pass one param at a time.

//Rewrite the function and curry it so we can
//pass one param at a time
var applyDiscount = curry(function(user, discount) {    
      user.discount = discount.code;    
      return user;
});

Step 3: let’s pass the first argument(maybeUser) to applyDiscount via “map”.

//pass the first argument to applyDiscount via "map"
const maybeApplyDiscountFunc = maybeUser.map(applyDiscount);//Note, since applyDiscount is "curried", and "map" will only pass 1 parameter, the return result (maybeApplyDiscountFunc) will be a Maybe wrapped "applyDiscount" function that now has maybeUser(1st param) in it's closure.In other words, we now have a function wrapped in a Monad!

Step 4: Deal With maybeApplyDiscountFunc

At this stage maybeApplyDiscountFunc can be:
1. If user actually exists, then maybeApplyDiscountFunc is a function wrapped inside a Maybe.
2. If the user does not exist, then maybeApplyDiscountFunc will be “Nothing” (subclass of Maybe)

If user doesn’t exist, then “Nothing” is returned and any further interaction with this are ignore completely. So if we pass 2nd argument, nothing happens. And also no Null errors are thrown.

But in the case where the user actually exists, we can try to pass the 2nd argument to maybeApplyDiscountFunc via “map” to execute the function like below:

maybeDiscount.map(maybeApplyDiscountFunc)! // PROBLEM!

Uh oh! “map” doesn’t know how to run function(maybeApplyDiscountFunc) when the function itself is inside a MayBe!

That’s why we need a different interface to deal with this scenario. It turns out that’s “ap”!

Step5: Let’s recap “ap” function. “ap” method takes another Maybe monad and passes/applies the function it’s currently storing to that Maybe.

So we can simply apply (“ap”) maybeApplyDiscountFunc to maybeDiscount instead of using “map” like below and it’ll work like a charm!

maybeApplyDiscountFunc.ap(maybeDiscount)//Internally it is doing the following because applyDiscount is store in the this.val of maybeApplyDiscountFunc wrapper:
maybeDiscount.map(applyDiscount)//Now, if maybeDiscount actually has the discount, then the function is is run.If maybeDiscount is Null, then nothing happens.

FYI: Apparently there is a change in the FL spec, The old version has (eg): `Just(f).ap(Just(x))` (where `f` is a function and `x` is a value) but the new version would have you write `Just(x).ap(Just(f))`But the implementations mostly haven’t changed yet. Thanks keithalexander

To summarize, if you have a function that deals with multiple parameters that might all be null, you curry it first, then put it inside a Maybe. Further, also put all params in a Maybe and then use “ap” to run the function.

curryN function

We are familiar with “curry”. It simply converts a function that takes multiple arguments to take them one-by-one.

//Curry Example:
const add = (a, b) =>a+b;const curriedAdd = R.curry(add);const add10 = curriedAdd(10);//pass the 1st argument. Returns a function that takes 2nd (b) parameter.//run function by passing 2nd argument
add10(2) // -> 12 //internally runs "add" with 10 and 2.

But instead of adding just two numbers, what if the add function can sum up all the numbers passed to it as an argument?

const add = (...args) => R.sum(args); //sum all the numbers in args

We can still curry it by limiting number of args using curryN like below:

//curryN example
const add = (...args) => R.sum(args);//CurryN Example:
const add = (...args) => R.sum(args);const add3Numbers = R.curryN(3, add);
const add5Numbers = R.curryN(5, add);
const add10Numbers = R.curryN(10, add);add3Numbers(1,2,3) // 6
add3Numbers(1) // returns a function that takes 2 more params.
add3Numbers(1, 2) // returns a function that take 1 more param.

Using “curryN” to wait for number of function calls

Let’s say we want to write a function that only logs if we call it 3 times (and ignore the 1st and 2nd call). Something like below:

//impure
let counter = 0;
const logAfter3Calls = () => {
if(++counter == 3)
  console.log('called me 3 times');
}logAfter3Calls() // Nothing happens
logAfter3Calls() // Nothing happens
logAfter3Calls() // 'called me 3 times'

We can simulate that using curryN like below.

//Pure
const log = () => {
  console.log('called me 3 times');
}const logAfter3Calls = R.curryN(3, log);//call
logAfter3Calls('')('')('')//'called me 3 times'//Note: We are passing '' to satisfy CurryN that we are passing some parameter.

Note: We’ll be using this technique in the Applicative validation.

Example 4— Collecting And Displaying Multiple Errors

Topics covered: Validation (aka “Validation Functor”, “Validation Applicative”, “Validation Monad”).

Validations are commonly referred as Validation Applicative because it is commonly used for validation using it’s “ap”(apply) function.

Validations are similar to Either Monads and used to work with composing multiple error-throwing functions. But unlike with Either Monad, where we typically use its “chain” method to compose, in Validation Monads, we typically use “ap” method to compose. And unlike either’s “chain” method, where we only collect the 1st error, “ap” method, especially in Validation Monads allows us to collect all the errors in an Array.

They are typically used in form validation where we may want to show all the errors at the same time.

Use case: We have a sign up form that validates username, password and email using 3 functions(isUsernameValid, isPwdLengthCorrect and ieEmailValid. We need to show all 1, 2 or 3 errors if they all occur at the same time.

In order to show multiple errors, use “Validation” Functor

OK, let’s see how to implement it using “Validation Applicative”.

We’ll use data.validation lib from folktalejs because ramda-fantasy doesn’t implement it yet.

Similar to “Either” Monad, it has two constructors: Success and Failure. These are like subclasses that each implement Either’s specs.

Step1: In order to use Validation, all we need to do is to wrap valid values and errors inside Success and Failure constructors (i.e. create instances of those classes).

const Validation = require('data.validation') //from folktalejs
const Success = Validation.Success
const Failure = Validation.Failure
const R = require('ramda');//Instead Of:
function isUsernameValid(a) {
   return /^(0|[1-9][0-9]*)$/.test(a) ?
          ["Username can't be a number"] : a
}//Use:
function isUsernameValid(a) {
   return /^(0|[1-9][0-9]*)$/.test(a) ?
        Failure(["Username can't be a number"]) : Success(a)
}

Repeat the process for ALL error throwing validation functions.

Step 2: Create a dummy function to hold validation success.

const returnSuccess = () => 'success';//simply returns success

Step 3: Use curryN to repeatedly apply “ap”

The problem with “ap” is that the left-hand side should be a functor (or a monad) containing function.

For example, let’s say we want to repeatedly apply “ap” like below. It will only work if monad1 contains a function. And the result of monad1.ap(monad2) i.e. resultingMonad is also a monad with a function so that we can “ap” to monad3.

let finalResult = monad1.ap(monad2).ap(monad3)//Can be rewritten as:
let resultingMonad = monad1.ap(monad2)
let finalResult = resultingMonad.ap(monad3)//will only work if: monad1 has a function and monad1.ap(monad2) results in another monad (resultingMonad) with a function

Generally speaking, we need 2 monads that has functions in order to apply “ap” twice.

In our case, we have 3 functions that we need to apply.

Let’s say we did something like below.

Success(returnSuccess)
       .ap(isUsernameValid(username)) //works
       .ap(isPwdLengthCorrect(pwd))//wont work
       .ap(ieEmailValid(email))//wont work

The above won’t work because Success(returnSuccess).ap(isUsernameValid(username)) will result in a value. And we can no longer continue to do “ap” on 2nd and 3rd function.

Enter curryN.

We can use curryN to keep returning function until it is called “N” number of times.

So we can simply do:

//3 coz we are calling "ap" 3 times.
let success = R.curryN(3, returnSuccess);

Now, the curried success keeps returning function 3 times.

function validateForm(username, pwd, email) {
   //3 coz we are calling "ap" 3 times.
   let success = R.curryN(3, returnSuccess);    return Success(success)// default; used for 3 "ap"s
       .ap(isUsernameValid(username))
       .ap(isPwdLengthCorrect(pwd))
       .ap(ieEmailValid(email))
}

Putting it all together:

If you liked the post by clicking on the ? it below and sharing it on Twitter! Thanks for reading! ??

LATEST: The Inner workings of the Browser — for JavaScript & Web Developers Use code: INNER15 and get 50% off!

Functional Programming

  1. JavaScript Is Turing Complete — Explained
  2. Functional Programming In JS — With Practical Examples (Part 1)
  3. Functional Programming In JS — With Practical Examples (Part 2)

ES6

  1. 5 JavaScript “Bad” Parts That Are Fixed In ES6
  2. Is “Class” In ES6 The New “Bad” Part?

WebPack

  1. Webpack — The Confusing Parts
  2. Webpack & Hot Module Replacement [HMR] (under-the-hood)
  3. Webpack’s HMR And React-Hot-Loader — The Missing Manual

Draft.js

  1. Why Draft.js And Why You Should Contribute
  2. How Draft.js Represents Rich Text Data

React And Redux :

  1. Step by Step Guide To Building React Redux Apps
  2. A Guide For Building A React Redux CRUD App (3-page app)
  3. Using Middlewares In React Redux Apps
  4. Adding A Robust Form Validation To React Redux Apps
  5. Securing React Redux Apps With JWT Tokens
  6. Handling Transactional Emails In React Redux Apps
  7. The Anatomy Of A React Redux App

Salesforce

  1. Developing React Redux Apps In Salesforce’s Visualforce