How does the compiler handle a boolean argument that has been partially applied?

I read an interesting article about boolean

function arguments :

https://medium.com/compileswift/clean-code-the-curse-of-a-boolean-parameter-c237a830b7a3

The author argues that in many cases it is better to split a function with an argument boolean

in two. This is due to the fact that using boolean parameters increases the cyclomatic complexity

code.

So, consider the following simple function:

let fbool (b: bool) (x: int) =
    if b then x
    else -x

      

Then define the function defined from the partial application like this:

let ftrue x = fbool true x

      

or equivalent

let ftrue = fbool true

      

Is the F # compiler smart enough to do what the author recommends, i.e. ftrue

, as defined above, a function that does not contain an operator if

and therefore does not contribute to circular complexity? Or does it contain a statement if

but always fetches a branch true

?

+3


source to share


3 answers


The F # compiler isn't smart enough to eliminate such branches. In general, compilers in .NET apply only a few optimizations in the hope that jitter will be optimized for them.

If you made the function fbool

inline ftrue

, it will look like this:

public static int ftrue(int v)
{
    bool flag = true;
    if (flag)
    {
        return v;
    }
    return -v;
}

      



The compiler should "obviously" eliminate branches here, but it is not.

However, you might be in luck and the code review tools understand that one of the branches will never be accepted and add that test to the CC score.

+5


source


The author of the article really talks about how clear the code is - the interaction between the programmer and the source code . This is almost always what is considered when people talk about cyclical complexity. It has nothing to do with the compiler output.

How the source code relates to what is being executed on the machine is another matter. Although eliminating a branch can improve performance, it does not remove branching from the code, and therefore the programmer is still dealing with cyclical complexity.



Partial application of the function you are describing is a good way to make the function of the function clearer at the point of use. Instead of passing in what might be a meaningless true / false value, you have two functions with meaningful names. It only helps if the names are more meaningful than true / false that they are not in your example toys.

However, this partial application does not reduce the cyclical complexity at all, since the source code still has exactly the same number of branches.

+3


source


I'm pretty sure the partial F # application is done in the simplest way, i.e. does not overwrite the function. It takes more compiler code than it costs to fbool true

turn into a function that just returns x

without an operator if

, but fbool false

turn into a function that -x

does. And you will only benefit from simple examples like this one that really need to be split into two functions.

Well, maybe there is another place where you would benefit: a function that has a single expression match

, for example:

let f optValue secondParam =
    match optValue with
    | None -> printfn "No optional param. Second parameter %A" secondParam
    | Some x -> printfn "Got an x value of %A, and 2nd param is %A" x secondParam

      

In this case, in f None

theory, it can be turned into a simple call printfn

(although I don't think the compiler will do it), but f (Some 3)

turn into another call printfn

.

However, even this example is contrived and hardly worth writing, because in most functions where you just do match

one parameter at a time, you put that parameter last (and possibly use a keywordfunction

). For example, in real code, my example above would probably look like this:

let f param = function
    | None -> printfn "No optional param. Required parameter is %A" param
    | Some x -> printfn "Got an x value of %A, and required param is %A" x param

      

In this case, the private application will not overwrite the function, because by the time you apply the parameter that is used in the expression match

, you applied the last parameter so that you are actually calling the function.

So, because real code is unlikely to ever benefit from this, to be worth the immense complexity of adding this compiler function (if there is other code before the statement if

, it is probably non-trivial to duplicate it into two functions), I'm pretty sure the F # compiler doesn't such a rewrite that you mention.

NOTE. ... I have not been able to include any information about this in a quick Google search, so it is possible that this answer is incorrect; if so, please uncheck and / or comment to let me know and I will edit to include correct information.

+2


source







All Articles