Skip to content

Instantly share code, notes, and snippets.

@jdegoes
Created October 8, 2014 17:20
Show Gist options
  • Save jdegoes/ed934ae6768519c1764e to your computer and use it in GitHub Desktop.
Save jdegoes/ed934ae6768519c1764e to your computer and use it in GitHub Desktop.
Death to Boolean Flags

Refactoring Booleans to Functions

Boolean parameters are a plague, responsible for non-composable, monolithic functions that never quite do enough, and countless programming bugs:

  • "Oops, meant to pass that as the 2nd boolean flag, not the 1st!"
  • "Oops, accidentally inverted the meaning of that boolean in the implementation!"
  • "Oops, the function isn't flexible enough for my use case, let's add a 6th boolean flag!"
  • "Oops, got the meaning of that boolean flag wrong, time to dig into the source code!"

All boolean parameters should be refactored into functions that effect the change otherwise encoded in the parameter.

This forces you to design a more composable API (try it and you'll see!), as well as reduces potential for user-error.

Examples

Please fork this Gist and add more and better examples!

Socket.setKeepAlive

This is a stupid example based on a broken, imperative Java API, but may be more relatable and requires less setup than a more realistic example in a purely functional application.

The setKeepAlive method accepts a boolean parameter indicating whether or not the socket should be kept alive.

Socket.setKeepAlive(on: Boolean)

socket.setKeepAlive(true)

Instead, we can change this method so you pass a timeout that can choose to keep alive or terminate the socket based on the value of the timeout (note: this is a change in semantics).

Socket.inactive(handler: Time => (Socket => Unit))

val KeepAlive = time => identity
val ShutDown  = time => (socket => if (time > 2000) socket.close() else ())

Socket.inactive(ShutDown)

jQuery.extend

The jQuery.extend function accepts a boolean flag indicating whether to do a shallow or a deep copy. Guess what 'true' and 'false' mean? Who knows and who cares, it's arbitrary and stupid.

jQuery.extend(true, foo, {bar: "baz"})

There are lots of ways to refactor this, here's one (I don't claim it's the best):

// jQuery.Extend :: (Json -> Json -> Json) -> Json -> Json -> Json
var Shallow = function(a, b) { return b; }
var Deep    = function(a, b) { return jQuery.extend(Deep, a, b); }

jQuery.extend(Shallow, foo, {bar: "baz"})
jQuery.extend(Deep, foo, {bar: "baz"})

The function you pass to extend is a semigroup that combines values of the same field name.

Summary

In both examples, the interface has become far more precise. Instead of having to guess at what 'true' and 'false' mean, and having to deal with the multitude of programming bugs that come from "programming by name", we pass logic where we used to pass single bits of information, logic that precisely and clearly defines what we want.

Beyond precision, which cuts down on programming bugs, the interfaces have also become more general-purpose. We can use them to do more things, which will further the reduce the necessity for "one-off" methods (or additional boolean flags) written for special cases.

@FranklinChen
Copy link

A boolean is just the simplest nontrivial algebraic data type, with 2 inhabitants. Are you ready to say that the trinary data type Color = Red | Yellow | Blue is to be banned also? I agree that boolean blindness is a problem, but removal of all booleans suggests you are advocating OO, whether of the single dispatch variety or of the multiple dispatch variety (in the case of replacing many booleans at once)? Also, some API boundaries do not permit passing of functions, but require parsing (say, of raw text or of command line arguments) and therefore booleans will be necessary before conversion to an appropriate richly typed representation.

@aryairani
Copy link

@FranklinChen I read it as

Color = Red | Yellow | Blue
setColor(Red)

is better than

def setColor(isRed: Boolean, isYellow: Boolean)
setColor(true, false)

even though they might do the same thing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment