Eliminate many conditionals with this one weird trick

Recently I attempted to classify the conditionals in software according to where in the code they originate. The first category of these was: Checking a value returned to me from code I own. A large proportion of these can be eliminated quite simply.

Imagine we want to begin reading a file at the last saved read position, which may be null if we haven’t read anything yet:

var readOffset = fileReader.GetSavedReadPosition();
if (readOffset == null)
  readOffset = new ReadPosition(fileReader, 0);

This code violates almost every rule in the book:

  • There is duplication, because both the caller and the callee have special code for the “no saved position” branch.
  • There is Connascence of Meaning, because both need a common understanding of what the special null value means.
  • We are violating the Tell, don’t Ask principle because we are making a decision on the basis of a value returned from a call to a different object.

All in all, this code has problems — and yet I see code like this everywhere I look. So, what to do?

Let’s look at this code from the Connascence point of view. The problem is the null value representing the special “not saved” case: both the caller and the callee have to agree to use null to mean that (hence “Connascence of Meaning”). Now, connascence becomes stronger with increasing distance. Our example smells strongly because the two connascent code fragments are in different classes; thus we could weaken it if we can bring the endpoints closer together.

Currently, the fileReader decides what to return when it has no saved read position, and the client code has to cope with that decision. What if, instead, the client code decides what it would like to get back in that case; what if it could simply tell the method what to return if it can’t do the thing we’re asking:

var readOffset = fileReader.GetSavedReadPosition(0);

Now the connascence has disappeared, and the conditional has disappeared with it. It’s as simple as that.

Many frameworks and APIs offer this kind of default return value parameter. For example, the standard Ruby Hash class provides this feature via the fetch method.

But what if there’s no sensible default value that we can pass into the called method? For example, consider the following case:

var username = auth.GetPrincipal();
if (username == null)
  throw new UsernameNotFound();
auth.SetPassword(username, password);

We still have Connascence of Meaning due to the null return case; and we don’t want to push the error handling (or whatever) down into the authentication object, because different callers may want to handle the situation differently. But we can take a leaf from the default parameter book, and have the authentication object execute code for us:

var throwIfNotFound = () => throw new UsernameNotFound();
var username = auth.GetPrincipal(throwIfNotFound);
auth.SetPassword(username, password);

Again, the null value has disappeared, and so has the Connascence and the conditional. For the sake of symmetry we can often also dispense with the return value altogether and pass in both code branches to the callee:

auth.WithPrincipal(
  (username) => auth.SetPassword(username, password),
  () => throw new UsernameNotFound());

lambda1

Pretty much every modern language supports this kind of code now:

  • Functional languages allow us to pass in functions to do the job; likewise Javascript;
  • C and C++ let us pass function pointers;
  • C# lets us use lambdas via Linq; Ruby lets us pass in lambdas, and even C++ (thanks @mmeijeri) and Java 8 now allow lambdas.

Some even let us use named parameters to make the options a little clearer; for example, we might write the above example in Ruby thus:

auth.withPrincipal(
  if_known: lambda {|username| auth.set_pass(username, passwd)},
  if_unknown: lambda {raise UsernameNotFound.new})

It’s as simple as that. By adopting these approaches it is possible to simplify a lot of code, effectively removing the connascence and the duplicated conditionals from the caller and the callee.

Advertisements

8 thoughts on “Eliminate many conditionals with this one weird trick

  1. In Smalltalk people do this a lot, and it looks even cleaner. E.g map at: key ifAbsent: [ .. ]. But in Java I tend to use Optionals instead. It lets the client choose how to handle an absent of a thing. E.g. if getPrincipal() returns an Optional then it will look like this:
    – auth.getPrincipal().orElseThrow(() -> new SomeException()) //it should have been there, maybe a programming error
    – auth.getPrincipal().orElse(“aDefaultPrincipal”); //the absent of the principal was expected, use a default
    – auth.getPrincipal().orElseGet(() -> auth2.getPrincipal().orElse(“default”)); // fallback to auth2, then use a default

  2. well, my opinion is that while it sounds like a good idea for the example he mentioned
    it’s going to be really messy in real-life code, it encourages nesting which reminds me with the callback hell in nodejs, your code will end up looking like a rotated pyramid.
    That’s why I believe that type-level Monads are the best option to maintain flatness (somehow though)
    like Option[T] and Future[T] and their friends.
    the idea about Option (or Optionals if you may) in particular is interesting because it legalises the case of non-existent values and allows you to compose code in seamless way (imagine mapping over a None, will basically do nothing)
    so eventually you will be eliminating conditionals.

    The main problem with Optionals in Java is that java generics are invariant, so, an Optional is not a sub-class of Optional so, using this pattern everywhere will kill polymorphism.

  3. Let’s step back and look at a higher level example. Consider another function is calling your code expecting a `ReadPosition` instance to render a visual mark on the last line the user has read. However, if the user didn’t read the file before, no marks should exist. In this case, I have no way to know if the “0” returned from “GetSavedReadPosition” is the default value sent by the caller, or it is really was “0”.
    In this case.
    I’d suggest using Option/Optional/MayBe (or whatever it is called), it is much more explicit than returning nulls, and it allows you to specify default values, or raise exceptions if you want.

  4. My opinion is that while it sounds like a good idea for the example you mentioned
    it’s going to be really messy in real-life code, it encourages nesting which reminds me with the callback hell in nodejs, your code will end up looking like a rotated pyramid.

    That’s why I believe that type-level Monads are the best option to maintain flatness (somehow though)
    like Option[T] and Future[T] and their friends. The idea about Option (or Optionals if you may) in particular is interesting because it legalises the case of non-existent values and allows you to compose code in seamless way (imagine mapping over a None, will basically do nothing)
    so eventually you will be eliminating conditionals.

    The main problem with Optionals in Java is that java generics are invariant, so, an Optional is not a sub-class of Optional so, using this pattern everywhere will kill polymorphism.

  5. Removing duplication is usually the right thing to do, and the first example does this pretty neatly.

    On the other hand I am trying to understand if the second piece of code you showed really benefits from this refactoring.

    def change_password(password)
    auth.with_principal(
    unknown: -> { raise UsernameNotFound },
    known: -> (username) { auth.set_password(auth.principal, password) }
    )
    end

    # vs…

    def change_password(password)
    raise UsernameNotFound if auth.anonymous?
    auth.set_password(auth.principal, password)
    end

    Although it’s certainly true that we didn’t get rid of the conditional, the duplication disappeared anyway (e.g. I made use of `anonymous?`) and that code tells a readable story.

    Another point of view could be that the `auth` object in this case is returning nil. Since we are interacting again with `auth`, we might make the caller ignorant of whether principal returns nil or not and just pass it to set_password. In that scenario, I would expect `auth` to know how to deal with nil, since internally it has a connascence (e.g. made an agreement) about the nil value, and therefore raise an exception whenever such “exceptional” case occurs. Then, we might catch and throw again the exception if we need to.

    def change_password(password)
    auth.set_password(auth.principal, password)
    rescue Authentication::UsernameNotFound
    raise MyApplication::UsernameNotFound
    end

    What do you think?

  6. I find this point key: ‘The problem is the null value representing the special “not saved” case’. Why encode “not saved” as a value when we can give it a more directly useful name?

    fileReader.FindSavedPosition(notSaved => ReadPosition.beginningOf(fileReader))

    Note: “find” saved position, because there might not be one.

    Broadly speaking, raising the level of abstraction matters more to me in these examples than anything else.

    As for withPrincipal(doThis, doThat, doTheOther), I like that withPrincipal() lists quite clearly the possible outcomes, whereas findPrincipal() returning Maybe[Principal] or some other type doesn’t make that quite as clear. Other than that, I consider one’s preference just that: a matter of style.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s