Free like a big ducky at sea.

It’s a (point-)free world

Where we compose to get rid of those pesky unnecessary parameters

Michel Belleville
Published in
4 min readFeb 5, 2019

--

me> Let’s compose!

wat> Pipelines not good enough for you anymore? 🤨 Want to get all fancy with your code?

me> Yes 🤗

wat> So, remember our last discussion how |> and <| can be used to pass parameters to a function in a flow?

me> 😀 that I do.

wat> Good ; keep that in mind, we’ll do something similar in a bit. But first, how about a magic trick?

addTwo : number -> number
addTwo parameter =
parameter + 2

wat> I’m gonna make this parameter… disappear.

addTwo : number -> number
addTwo =
(+) 2

wat> Tadaaaa! It’s… gone… 🤡

me> Enough with the clown! 😠 How did you do that? 😲

wat> That’s the trick: we didn’t need this parameter. You know that even though + looks like a special little snowflake operator, it’s really a fancypants “infix” function…

me> A function that takes two parameters on both sides, yes ; and (+) is the same as + except as a normal function it takes its parameters one at a time after it like (+) 2 3.

wat> So, considering the signature for (+) is:

(+) : number -> number -> number

wat> What would be the signature for ((+) 2)?

me> Well, you’re feeding it its first parameter so I guess it’s:

(+) : number -> number -> number
((+) 2) : number -> number

me> But is that even allowed, feeding a function some of its parameters and not all at once?

wat> It is in Elm (and quite some other functional languages). It’s called “partial application” (since you can apply as few or as many of the function’s parameters as you want, the result being either the function’s end result or another function with the remaining parameters ready to be applied). So, what did we do there?

addTwo : number -> number
addTwo =
(+) 2

me> Hmm… We’re saying that addTwo is still a function that takes a number to give us back another number, and is the same as using (+) with 2 as the first parameter?

wat> 👏 This is what we call being the “point-free style”, “points” being the unnecessary parameters.

me> But what has it got to do with function composition? And those new operators you’ve told be about, << and >>?

wat> That’s the second part of the magic trick. Say we took our String-converting, Maybe number-mapping and defaulting and made a function out of it:

addTwoToAStringMaybeOrDefaultToZero : String -> number
addTwoToAStringMaybeOrDefaultToZero string =
string
|> String.toInt
|> Maybe.map (\number -> number + 2)
|> Maybe.withDefault 0

me> With you so far…

wat> Abracadabra.

addTwoToAStringMaybeOrDefaultToZero : String -> number
addTwoToAStringMaybeOrDefaultToZero =
String.toInt
>> Maybe.map ((+) 2)
>> Maybe.withDefault 0

me> Whaaa?… Ok, you’ve used the “point-free” trick with Maybe.map ((+) 2)… It’s almost like the original (|>)-based pipeline but with a (>>)… and this time you give String.toInt to Maybe.map ((+) 2) and the result of this to Maybe.withDefault 0… it’s weird!

wat> Look at the signature for(>>):

(>>) : (a -> b) -> (b -> c) -> (a -> c)

me> So…
- I take on the left a function that takes an a and gives back a b
- then I take on the right a function that takes a b and gives back a c
- I return a function that takes an a and gives back a c
So, it takes both the functions and makes another one that’s the same as executing the first one, give its result to the second, and return that last result?

wat> 😉 you’ve got it. Usually, since returning a function is always what happens when you apply the parameters partially, the signature is often written like this:

(>>) : (a -> b) -> (b -> c) -> a -> c

me> So, it’s like the pipeline, only instead of passing the parameter to the functions directly, it produces a new function that acts like both functions chained as soon as we give it its arguments?

wat> 😃

me> Ok, let me hazard a guess as to the signature of (<<)

(<<) : (b -> c) -> (a -> b) -> a -> c

wat> Yes, exactly like (<|) is basically (|>) with the arguments “flipped”, (<<) is the same as (>>) with the arguments flipped, and we could have written our pipeline like this:

addTwoToAStringMaybeOrDefaultToZero : String -> number
addTwoToAStringMaybeOrDefaultToZero =
Maybe.withDefault 0 << Maybe.map ((+) 2) << String.toInt

me> So, this is function composition in Elm?

wat> Yes. You will often find it is exactly the same as mathematical function composition (only math people -and Haskell people- like their function composition written like: f . g where f and g are the functions and (.) is the same as (<<)).

me> Did you just trick me into doing math? 😈

wat> Maybe… was it painful? 😊

me> No… still, I’ll need a bit of time to digest this I think.

wat> Fair enough. Read you around 😃

--

--