Lifting functions into monadic context of algebraic structures is quite practical pattern. This article will prove it's practicality and when you finish reading it, you will have another FP tool to conquer the complexities of the imperative code and create yet more elegant functional code. I will assume reader's basic understanding for concepts of functional programming and algebraic structures. I will use monet.js as a JavaScript implementation of a Monad algebraic structure and ramda as functional library containing some nifty utils to manipulate the Monads.

Let's say we have a programming challenge. We want to add two numbers together to create another number. Well it's not really a challenge for most of you, and is it quite a primitive problem to solve.

// add :: (Number, Number) -> Number const add = (a, b) => a + b;

Here, there it is. Quite simple. Same type of input and output. It is very easy to understand and reason with. In category theory we call this endomophism. Only numbers should go inside this function and only numbers should go out.

Now we need some function to parse some user input and run it through our add function. As it turns out there is already such a function in JavaScript. It's called parseInt.

const userInputA = '1' ; const userInputB = '2' ; const a = parseInt (userInputA, 10 ); const b = parseInt (userInputB, 10 );

But what if our user input contains some junk instead of numbers. We may end up with the following.

const userInputA = 'junk' ; const userInputB = '2' ; const a = parseInt (userInputA, 10 ); const b = parseInt (userInputB, 10 );

Houston, we have a problem! Constant a now contains NaN. In category theory our function add can process input from category Number. But NaN is not part of that category and though the behavior of add will be undefined.

Maybe monad to the rescue

Maybe is a monad that contains some value or nothing . Basically it is a type safe container for our parsed value. We can lift JavaScript's parseInt function to return Maybe monad.

const { Maybe } = require ( 'monet' ); ﻿ const userInputA = 'junk' ; const userInputB = '2' ; const safeParseInt = (value, radix = 10 ) => { const parsed = parseInt (value, radix); return isNaN (parsed) ? Maybe.Nothing() : Maybe.Some(parsed); }; const a = safeParseInt(userInputA); const b = safeParseInt(userInputB);

Now that we have our parsed values locked in type safe containers we need to add them together. But how do we that ? Actually the Maybe implement the Apply spec and therefore contains ap method that allows us to combine multiple monads together and map them over a function.

const { curry } = require ( 'ramda' ); ﻿ const { Maybe } = require ( 'monet' ); ﻿ const userInputA = '1' ; const userInputB = '2' ; const safeParseInt = (value, radix = 10 ) => { const parsed = parseInt (value, radix); return isNaN (parsed) ? Maybe.Nothing() : Maybe.Some(parsed); }; const a = safeParseInt(userInputA); const b = safeParseInt(userInputB); const add = (a, b) => a + b; ﻿ const addC = curry(add); const added = a.ap(b.map(add));

This seems quite ugly and unpractical. Let us create a better version of this code.

const { liftFN } = require ( 'ramda-adjunct' ); ﻿ const { Maybe } = require ( 'monet' ); ﻿ const userInputA = '1' ; const userInputB = '2' ; const safeParseInt = (value, radix = 10 ) => { const parsed = parseInt (value, radix); return isNaN (parsed) ? Maybe.Nothing() : Maybe.Some(parsed); }; const a = safeParseInt(userInputA); const b = safeParseInt(userInputB); const addM = liftFN( 2 , add); const added = addM(a, b);

What have we just done ? Well, we used liftFN from ramda-adjunct. It takes an arity and a function and returns an homorphic autocurried function lifted into monadic context with specified arity. Or in a lame man's terms: takes the inputs as monads, unwraps the the values from the monads, applys it to original function and wraps the result into compatible monad and returns it. This is what I call heavy lifting.

Insight into heavy lifting

Ramda also contains liftN util. Why didn't we use it ? Well there is think called fantasy-land specification. This specification tells us how to implement algebraic structures so that they can be interoperable. Ramda <= 0.23.0 implements an obsolete version of this spec and that is why it is not compatible with monet.js. Today I have released liftFN that acts as an interop for Ramda <= 0.23.0 and monet.js <= 0.8.1. Next version of Ramda will be again compatible with fantasy-land specification and next version of monet.js will also be compatible with fantasyland spec thanks to PR #112, making it compatible with Ramda. For the time being use liftFN from ramda-adjunct to circumvent this inconvenience.

This is the topmost level the elevator goes today ;] Remember write pure testable functions that works with JavaScript native types and lift them later into monadic context (if you need to). And compose, compose, compose... !