Fantasy Birds: Bluebird

Bluebird

bluebird (aka B combinator), which is written:

(b -> c) -> (a -> b) -> a -> c
// f => g => a => c => f(g(a)))

The bluebird combinator represents function composition, which is probably the most basic (and maybe important, too) concept in functional (composable) programming. I’m not going to try to define and deep-dive into what function composition is here, but the core idea here is that you have two functions, one (f) which takes an a and returns a b, and one (g) which takes a b and returns a c, where f accepts as its argument the result of g. As you may be able to see, the value you pass in (a) flows through the functions from right to left.

In a totally imperative form, it could look like this:

const someMath = a => {
  const fn1 = a => a+1
  const fn2 = b => Math.floor(b/2)
  const output1 = fn1(a)
  const output2 = fn2(output1)
  return output2
}

Do you see how fn2 depends on the output of fn1? I’m sure this is a familiar pattern. So what is wrong with this? Not really anything, I guess, but in taking a, adding 1, and then dividing by 2, (basic math, right?) you’ve introduced one of the most difficult things in programming: Naming Things. Take this example as an alternative:

const someMath = a => bluebird(b => Math.floor(b/2))(a => a+1)(a)

How many intermediary values? None.

Bluebird_

bluebird_ (aka B` combinator), which is written:

(a -> c -> d) -> a -> (b -> c) -> b -> d
// f => a => g => b) => f(a)(g(b)))

The bluebird_ combinator is a slightly more abstract version of bluebird in that it still performs function composition, but the outer function (f) is a higher-order function in that it returns a function. This can be useful (among many other situations) when you really want to avoid intermediary values. Consider the following examples:

const bluebird = (b -> c) -> (a -> b) -> a -> c
const add = a => b => a+b
const addN = n => add(n)
const add10 = addN(10)
["1", "2", "3"].map(bluebird(add10)(parseInt))
// [ "11", "12", "13" ]
const bluebird_ = (a -> c -> d) -> a -> (b -> c) -> b -> d
const add = a => b => a+b
const addN = n => add(n)
["1", "2", "3"].map(bluebird(addN)(10)(parseInt))
// [ "11", "12", "13" ]

Granted there are no big wins here, but I guess this is a pretty contrived example. The key here, though, is that you can avoid declaring these intermediary values (the function add10 here) that don’t necessarily have any value on their own.

Try It On Your Own

Can you use bluebird to compose a higher-order function? ie: A function that accepts a function and/or returns a function?

Since both bluebird and bluebird_ compose together only two functions, can you make a function using either or both to create a composition of more than two functions? How about an odd number of functions vs and even number?

Next up: bluebird