Search code examples
recursionsyntaxf#implicit-typing

F# implicit typing choking on simple recursion


When I define a recursive function in F# thus:

let rec recursiveSum inputs =
    let startState = 0.0m

    if List.length inputs = 1 then
        startState + inputs.Head
    else
        let t = List.tail inputs
        startState + inputs.Head + recursiveSum t

...all is good. When I attempt to avoid the "empty list" problem thus:

let rec recursiveSum inputs =
    let startState = 0.0m

    **if List.isEmpty inputs then startState**

    if List.length inputs = 1 then
        startState + inputs.Head
    else
        let t = List.tail inputs
        startState + inputs.Head + recursiveSum t

...I get yelled at:

recursion.fsx(5,9): error FS0001: This expression was expected to have type
    unit    
but here has type
    decimal

What am I missing here?


Solution

  • From the docs:

    The types of the values produced in each branch must match. If there is no explicit else branch, its type is unit. Therefore, if the type of the then branch is any type other than unit, there must be an else branch with the same return type.

    You're missing said else.

    let rec recursiveSum inputs =
        let startState = 0.0m
    
        if List.isEmpty inputs then 0.0m
        elif List.length inputs = 1 then
            startState + inputs.Head
        else
            let t = List.tail inputs
            startState + inputs.Head + recursiveSum t
    

    (N.b. I've used elif here rather than nesting another if expression; hopefully that's not too much of a distraction.)

    That said, your logic involving startState is highly suspect; it's always zero, and really serves no purpose here. Your state should probably be a parameter rather than a local value so it can be used as an accumulator:

    let recursiveSum inputs =
        let rec impl state inputs =
            if List.isEmpty inputs then state
            elif List.length inputs = 1 then
                state + inputs.Head
            else
                let t = List.tail inputs
                impl (state + inputs.Head) t
        impl 0.0m inputs
    

    Lastly, let's make it idiomatic:

    let recursiveSum inputs =
        let rec impl state inputs =
            match inputs with
              | []   -> state
              | [h]  -> state + h
              | h::t -> impl (state + h) t
        impl 0.0m inputs
    

    which can be shortened to

    let recursiveSum inputs =
        let rec impl state = function
          | []   -> state
          | h::t -> impl (state + h) t
        impl 0.0m inputs