
Get rid of boolean function parameters - ingve
http://mortoray.com/2015/06/15/get-rid-of-those-boolean-function-parameters/
======
AndrewDucker
One of the things I wish C# did was allow for inline declaration of
enumerations in method parameters.

Something like:

    
    
        void DoSomething(Thing thing, enum logging{WithLogging,NoLogging}){}
    

Because I'd frequently like to be able to make my parameters more explicit,
but the overhead of creating a new enum type seems a tad heavyweight.

Edit: Raised it as an issue on GitHub with more details -
[https://github.com/dotnet/roslyn/issues/3497](https://github.com/dotnet/roslyn/issues/3497)

~~~
jjaredsimpson
Why not just use named parameters at the call site instead?

    
    
        DoSomething(thing, logging: true);

~~~
ColinDabritz
I definitely like this approach in my own code. I think the main advantage of
a different approach such as enums is enforcing the explicitness at the call
site for all users, especially in library functions.

Another thing to consider: In some cases, it may make more sense to split the
function into two versions, one for 'true' and one for 'false'. Then you gain
the enforced explicitness. The function could call a private method to share
code and pass the Boolean. Clearly that isn't ideal for all situations, but it
might help in some situations today.

~~~
JadeNB
> Another thing to consider: In some cases, it may make more sense to split
> the function into two versions, one for 'true' and one for 'false'. Then you
> gain the enforced explicitness. The function could call a private method to
> share code and pass the Boolean.

The author describes exactly this approach and implementation, under the
heading "Distinct functions".

------
greggman
So I get that

    
    
        calc_formula(a, b, false) 
    

Is unclear but so are `a` and `b`

    
    
        ctx.arc(100, 200, 300, 0, Math.PI*2, false)
    

or

    
    
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, 100, 200, 0,
                      gl.RGBA, gl.UNSIGNED_BYTE, null);
    

How are those examples any more self documenting? I don't see anything special
about boolean anymore than the other arguments. If you really want your code
to be clear (and I'm not saying I do this) you'd need to either use a language
that allows naming every argument or else put every argument in a variable
with a descriptive name

    
    
        var centerX = 100;
        var centerY = 200;
        var radius = 300;
        var arcStartRadians = 0;
        var arcEndRadians = Math.PI * 2;
        var counterClockwise = false;
        ctx.arc(centerX, centerY, radius,
                arcStartRadians, arcEndRadians,
                counterClockwise);

~~~
paulmd
I'll say that I do that (define constants to variables/consts/#DEFINE/etc and
then use them as arguments), and I do it for exactly the reasons you describe.
I also try to make magic numbers as self-explanatory as possible - rather than
(say) use the number 84600 I'll write 60 * 60 * 24 for when I want the number
of seconds per day.

It's way easier to remember what's going on when you come back a year later,
and all but the simplest compilers will be smart enough to bash it down to
constant parameters if that's possible.

Yeah, more lines of code, but I think readability trumps terseness (within
reason). Some functions just use a bunch of parameters and it's not really the
extra line to name the parameter that's causing the issue. Also if you're
using an editor that does code folding you could just fold that block away.

~~~
chrisheller
I'm not sure if the transposition was intended, but your comment shows another
reason why using a calculated value here is good.

------
darklajid
That comes up again and again. For me, named arguments solve that problem.

    
    
        float CalcFormula(int a,int b, bool isGain) {..}
    
        var x = CalcFormula(4, 5, isGain: true);

~~~
reacweb
You may love Ada:

    
    
        function Calc_Formula(A, B : Integer; Is_Gain : Boolean) return Float is ...
    
        X : Float := Calc_Formula(4, 5, Is_Gain => True);

~~~
darklajid
Actually that was 'real' C#, so the language I'm working with does support
this feature.

That said, Ada is certainly something I want to learn more about at some
point.

------
ignoramous
I think booleans (flags) are unavoidable at times. Consider a case where you
want a flag to switch verbose logging on and off selectively. Writing an enum
is a viable solution, but heavy weight nonetheless compared to the primitive
boolean-flag based approach.

I agree with the author that with flags in fn-signature, the code becomes
tough to reason about at the call-site; but a preferred solution, IMO, is to
use "named arguments" or "labeled arguments" in the languages that allow them
and/or write proper doc-strings in languages that don't.

For instance in OCaml you'd do (note: specifying types is optional):

    
    
        let calc_formula (a :int) (b :int) ~(is_gain :bool) :float =
          if is_gain then
            (* something slightly different *)
    
          (* common stuff *)
    

You'd call the above as:

    
    
        calc_formula 10 20 ~is_gain:true
    

Optional arguments allow for a terser call-site syntax at the cost of implicit
behaviour:

    
    
        let calc_formula ?(is_gain=true) a b  = ...
    

You could then call the above fn:

    
    
        calc_formula 10 20
        calc_formula 10 20 ~is_gain:false

~~~
s_kilk
> I think booleans (flags) are unavoidable at times. Consider a case where you
> want a flag to switch verbose logging on and off selectively.

In my experience, even these situations tend to grow arms and legs pretty
quickly, so you end up with a 'verbose' boolean that accepts four values:
True, False, "superverbose" and "off". Then you notice that in some places the
function is being called with an array as the value for 'verbose', because
you're in a language where arrays are implicitly cast to Boolean (I'm looking
at you python).

There are too many cases where the optional parameter is not really a boolean,
but merely a sum type which coincidentally only has two members at the moment,
but is likely to grow to more members.

~~~
ignoramous
I have seen these cases crop up myself. But... as Martin Fowler would say,
YAGNI... you ain't going to grow arms and legs...

------
eru
Compare Boolean Blindness
([https://existentialtype.wordpress.com/2011/03/15/boolean-
bli...](https://existentialtype.wordpress.com/2011/03/15/boolean-blindness/)).

~~~
reagency
Came here to post this. This a rather deep analysis of of the semantics of
Boolean abuse.

------
aleksi
Several years ago I found this Little Manual of API Design, and found it very
useful. It discusses boolean arguments among other things.
[http://www4.in.tum.de/~blanchet/api-
design.pdf](http://www4.in.tum.de/~blanchet/api-design.pdf)

------
pablovidal85
Ugh, what about avoiding flags entirely? I think "slightly" different
behaviour is a smell of god-functions. Why not split the functionality in two,
perhaps composable, functions?

~~~
whichdan
This would be a great case for pattern matching in Elixir.

------
riquito
Named parameters are nice in this case, e.g. in Python

    
    
        def calc_formula(a, b, is_gain=False):
            ...
        
        calc(a, b, is_gain=True)

~~~
moreati
Also worth mentioning that Python 3 supports keyword only parameters, which
enforce this calling styling [http://blog.gahooa.com/2009/12/08/python-has-
keyword-only-pa...](http://blog.gahooa.com/2009/12/08/python-has-keyword-only-
parameters/) e.g.

    
    
        def calc_formula(a, b, *, is_gain=False):
            ...

------
tempodox
My guess is: The fact that Booleans only encode a single distinction fools us
into thinking we can be sloppy with naming their values or the places those
values are stored (not just function parameters). In any given context, that
distinction may seem trivial.

It takes a good deal of practise to know from experience how wrong that would
be. So take my advice and always have a copy of this article under your pillow
:)

------
amarraja
In an old job, I saw some code like this

    
    
        refunder.Refund(orderId, customerEmail, true, false, payment=="visa", order.shipping == 0, true, true, order.day == "sunday")
    

Not verbatim, but you get the idea -- and yes, the refund method was even more
confusing than then invocation

------
lordnacho
Swift can help with this. There's certain ways to specify that a parameter
must be named.

------
rsp1984
Even simpler: Just use a const variable instead of a constant literal in the
caller code:

    
    
      const bool useWayA = true;
      int result = foo(a, b, useWayA);
    

instead of

    
    
      int result = foo(a, b, true);

~~~
mortoray
This would essentially be the enum option: giving names to constants. But this
way wouldn't enforce the convention, you could still use `true` or `false`.

------
yellowapple
Wouldn't the use of named parameters solve the confusion aspect that's cited
as the reason to not use boolean parameters? Not that the use of enums or
distinct function wrappers is a bad approach (it's probably more elegant), but
the hash approach does eliminate quite a bit of ambiguity.

With languages that include full pattern matching support and atom/symbol
literals (like Erlang and friends, for example, and like most other functional
languages IIRC), you could alternately match on a particular symbol instead of
relying on a boolean flag.

------
mtimokhin
Even in PHP (no named arguments):

    
    
        class Route
        {
           const RECURSIVE     = true;
           const NON_RECURSIVE = false;
    
           public function __construct($pattern, $recursive = Route::NON_RECURSIVE) {
             // ...
           }
         }
         $route = new Route($pattern, Route::RECURSIVE);

~~~
hewhowhineth
Why not

    
    
       $route = new Route($pattern, $recursive = true);

------
JoeAltmaier
Or hover your mouse over the call, and see the prototype in a popup. Its maybe
easier than visually parsing that complex enum form.

The problem with enums as class members is, they have to be scoped when used.
I wish that method invocation included the scope of the class being called on.

------
wazari972
the problem with the "distinct functions" case is if end up doing this:

    
    
      if is_gain: calc_formula_is_gain(a, b)
      else: calc_formula(a, b)
    

However a completly agree it makes sense with sin/cos functions.

------
monk_e_boy
I hate to be _that_ person, but these are lessons in coding that we learn
during the 'self documenting code' life lesson. Being part of any team and
you'll find yourself doing these sort of changes during code reviews. Not
quite refactoring, more spit and polish.

~~~
airza
I'm not really sure what the point you're trying to make is; What other
purpose would a software blog have than to share information gained through
experience?

Is it that the information is not helpful to others, because it is not helpful
to you?

~~~
monk_e_boy
I guess I forget that there are a whole bunch of people on here that don't
code _at all_. I think is really is lesson two, the one where you learn about
functions and what to name them and about parameters.

It's not helpful to anyone who has written more than a few lines of code.
Which is why I replied in surprise. HN doesn't let me down vote, so I comment.

I like to think that stuff on here should represent what hackers (coders)
would find interesting. Advanced coding, distributed systems, the other wooly
things like marketing, people skills etc. I didn't think this fell under the
scope of _hacker_ news. It's not news to any hacker.

~~~
joshuacc
I disagree. I've worked with people who have written tens of thousands of
lines of code, but didn't see anything wrong with throwing a boolean parameter
into the mix. It's not that they were bad developers in other ways. It's just
that they valued immediate productivity over long term maintainability.

