Search code examples
arraysf#discriminated-union

How to define a zero element when using discriminated unions in F# Array functions


OK, a question, I would like to use an array of discriminated unions in the Array functions. In the code below, I define a type ResultVari that is either Unknown or a floating point value. I also define the infix plus operator for the type, which returns a value only if both args are not Unknown. This works fine.

type ResultVari =
    | Unknown
    | Value of float
    static member (+) (a,b) =   // add two ResultVari's together
        match a,b with
        | Value(av),Value(bv) -> Value(av + bv)  // only has a value if both args do.
        | _ -> Unknown

(* Summation of array of ResultVari, such that if any are unknown then the result is Unknown *)
let example1 = [| Value(4.0); Value(5.5); Value(3.1) |]     // summation should be 12.6
let example2 = [| Value(4.0); Unknown; Value(3.1) |]        // summation should be Unknown

let summation (varArray:ResultVari array) =
    Array.sum (+) varArray       //ERROR this value is not a function and cannot be applied

let summation2 (varArray:ResultVari array) =
    Array.fold (+) (Value(0.0)) varArray       // Works

let sum_example1 = summation2 example1
let sum_example2 = summation2 example2

printfn "%A" sum_example1  // 12.6
printfn "%A" sum_example2  // Unknown

Using summation2 the program works as expected with the sum for example1 being 12.6 and for example2 being Unknown.

But I don't understand why summation doesn't work - the compiler complains "this value is not a function and cannot be applied". In another attempt (not shown), I also got the error of a missing get_Zero element which I understand -- the sum function has to use some type of a zero definition to start the summation, and using the fold function with my Value(0.0) as the start value like in summation2 solves that.

So is there a way to define a get_Zero element for a discriminated union, or would I have to use a record type instead for ResultVari? Then I could use Array.sum instead of using Array.fold.


Solution

  • You need to add a zero element to use array.sum - like this:

    type ResultVari =
        | Unknown
        | Value of float
        static member (+) (a,b) =   // add two ResultVari's together
            match a,b with
            | Value(av),Value(bv) -> Value(av + bv)  // only has a value if both args do.
            | _ -> Unknown
        static member Zero with get() = Value(0.0)
    

    Then the code becomes:

    let summation (varArray:ResultVari array) =
        Array.sum varArray
    

    This makes sense because when you sum something you need to start from zero and without the zero member, the compiler doesn't know where to start.