Programming

Get rid of those boolean function parameters

I need to make a quick variation of a function. The calculation, or behaviour, differs slightly on the needs of the caller. I throw in a bool parameter to do this switch. It’s fast and easy, yet I’m almost always disappointed later. It’s a bit hard to read from the call side. An enum, or distinct functions, would be cleaner.

Setup

The basic example here is you have a function:

1
2
3
defn calc_formula = ( a : int, b : int )->(:float) {
    ...
}

And you have a new call-site that needs to slightly alter the behaviour. So you stick a boolean on to trigger that switch:

1
2
3
4
5
6
7
defn calc_formula = ( a : int, b : int, is_gain : bool )->(:float) {
    ...
    if (is_gain) {
        //something slightly different
    }
    ...
}

The function itself looks fine, but what happens to the caller?

1
var v = calc_formula( ia, ib, true )

When I’ve just modified the calc_formula function then I’ll know what that true means. But what happens when I come back months later. Or what if somebody else sees this code. They’ll have to flip to the function definition to understand the mysterious true value. It’s not a typical parameter to this formula, thus it can’t be inferred from context.

Enum

This looks easier to read:

1
var v = calc_formula( ia, ib, calc_formula_type.is_gain )

I can immediately see I’m using an is_gain variant calculation. All I’ve done is swap out the boolean parameter for an enum.

1
2
3
4
5
6
7
8
enum calc_formula_type {
    standard
    is_gain
}

defn calc_formula = ( a : int, b : int, opt : calc_formula_type )->(:float) {
    ...
}

I’d like a language that can contextually resolve enums, so I can just type something like calc_formula( ia, ib, is_gain ). Having to remember the enum type names is kind of annoying. I’ll probably have some way to do this in Leaf.

Distinct functions

An alternative is to use distinct function wrappers and hide the underlying function.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
defn calc_formula = ( a : int, b : int ) -> {
    return calc_formula_impl(a,b,false)
}

defn calc_formula_is_gain = ( a : int, b : int ) -> {
    return calc_formula_impl(a,b,true)
}

defn calc_formula_impl = ( a : int, b : int, is_gain : bool ) -> {
    ...
}

The callers look good: calc_formula(ia, ib) or calc_formula_is_gain(ia, ib). It still involves a boolean on the actual function implementation. Though it isn’t as bad since the only callers of the calc_formula_impl know about it, and they’re defined right beside it. The context makes it easy to understand that the boolean parameter is.

Deciding which to use

Both solutions make the calling code easier to understand.

The distinct functions form comes at the cost of boilerplate wrapper functions — I’m never a fan of boilerplate code. However, if the formula really is distinct, then it makes sense to have separate functions. If the two functions truly feel like two separate functions then I do prefer this form.

Consider for example sin and cos. They are basically the same function with a phase offset. I’d truly hate to see something like this in code:

1
var q = sin( angle, sin_mode.cos )

Sure, the implementation is nearly identical, but they feel like very distinct functions.

I also don’t like flags that change the purity of the function:

1
2
3
var bond = calc_bond( params );

var rbond = calc_bond( params, bond_mode.register_global );

The second form both calculates a bond and registers it in a global table, whereas the first bond is a clean function without any side effects.

That is, I put up with the boilerplate code if the flags approach “feels” wrong.

Virtuals

If the function is a virtual member in a class I’ll nonetheless lean toward the flags approach. A series of functions can often place a burden on derived classes: the boilerplate multiplies.

A nice combined solution is to make a protected enum virtual and expose the wrapper functions in the base class.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class shape {
    defn calc_nominal_bounds = -> {
        return calc_bounds( bounds_type.nominal )
    }

    defn calc_render_bounds = -> {
        return calc_bounds( bounds_type.render )
    }

    protected abstract defn calc_bounds = ( how : bounds_type ) -> ( : rect );
}

This lets me call the functions with the nice form my_square.calc_render_bounds() but avoids having to overload multiple definitions in each derived class.

Categories: Programming, Use Case

Tagged as:

13 replies »

  1. In Python, you can actually use the name of the parameter inside the function call:

    v = calc_formula(ia, ib, is_gain=True)

    Making it easy to understand yet still using standard boolean.

  2. Named parameters (at call site) could perhaps also be an option if the language supports them.

  3. I think your distinct function approach is closer to the answer. Named arguments, if you think about it, are an anti-patterns, because they almost always imply an “if” expression in the body, which makes it complex for no good reason. IMO you should write functions as small as possible, and compose them to achieve complex behavior. That should yield less “if”s, less named or boolean arguments.

    • I in general like avoiding `if` as well. But most of the common languages actually don’t provide good ways to do function comprehension, especially where it is something in the middle of the function that needs to behave differently.

      Closures and lambdas help, as do C++ templates, but it still often feels very difficult to do basic function patterns.

  4. For using an enum with a virtual function I don’t like the fact that a derived class cannot extend the enum.

  5. In the calc_formula_impl case, I like it when the language easily allows to make the _impl function private to the two other functions. For example in OCaml:

    let calc_formula, calc_formula_is_gain =
    let calc_formula_impl is_gain a b = … in
    calc_formula_impl false, calc_formula_impl true

  6. What is function comprehension? Is it like a list comprehension in functional programming?

  7. most languages (without named parameters) can do something like
    var is_gain
    var v = calc_formula( ia, ib, is_gain=true )

    • Ha! That’s clever. It looks like that would work in C, in which assignment operations are expressions.

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