Are You Flying First Class?

15-Jan-2020
  • Functional Programming
  • JavaScript
  • Partial Application
  • Curry
  • Composition
1 min read / 319 words

What does it mean for a thing in a programming language to be first class? It means it is a value. Why is this important? You cannot compose operators in non functional first languages. If you played with legos as a kid...ahem...or as an adult, then you know how cool composition can be!

For example in JavaScript, how would you compose the ternary operator with other functions?

We will use the compose function from the popular JS library Ramda:

import * as R from 'ramda'
const isTruthy = (x) => Boolean(x)
R.compose(? :, isTruthy)(false) // THIS FAILS

This fails because the first argument is not first class (a value)! The saving grace in JS, is that you can get around this by wrapping the operator in a function (which is first class):

R.compose(x => (x ? 'hi' : 'bye'), isTruthy)(true); // 'hi'

This is great that we have functions at our disposal to let us convert something into a first class value. But what if you had the value 'hi' ahead of time, but wanted to compute the value of 'bye' at run time? One common use case is if your user gave you a list of settings at run time but you had set up some good defaults ahead of time. This is a contrived example, but we can achieve this like so:

const sayBye = predicate => greeting => (predicate(greeting) ? greeting : 'hi');
R.compose(sayBye(isTruthy), x => x.greeting)({ greeting: 'bye' }); // 'bye'

But since this ternary concept ("do this or that") based on a condition is so common, we can abstract the ternary operator into a function:

const ternary = predicate => y => z => data => (predicate(data) ? y(data) : z(data));
ternary(() => false)(() => 'hi')(x => x.greeting)({ greeting: 'bye' }); // 'bye'

We need the arguments to be functions so we can access the last argument data if need be. And we curry here so that we can pass one argument at a time because we may not have all the details up front. This is known as partial application which can be super useful!

Using just Ramda functions:

R.ifElse(R.F, R.always('hi'), R.prop('greeting'))({ greeting: 'bye' }); // 'bye'

You don't have to go the full blown Ramda route. However it is quite helpful to have this distinction in mind of what is first class and not. It opens up a whole new world of possibilities like partial application and function composition!