Question

I'm just learning F#, and while playing at tryfsharp.org I noticed that if I change this code:

[0..100]
|> List.sum

to

["A"; "B"; "D"]
|> List.sum

I get the following error:

The type 'string' does not support the operator 'get_Zero'

(Here's the script that you can run/amend in your browser, though it only seems to work in IE for me!)

When I checked the definition of List.sum; it says that the type must have a static member called Zero. This seems to explain the error; except for the fact that I can't see any member called Zero on int!

So; where is this Zero member that applies to ints? I can't see it in intellisense if I type int., nor in the docs, which says int is just a .NET System.Int32 (which doesn't seem to have a static Zero property).

(note: it does say "Operator" and not "Member" in the error; which may be related; though the List.sum definition does just say "member").

Was it helpful?

Solution

Digging in F# source code, List.sum (and Seq.sum) is using GenericZero:

let inline sum (source: seq< (^a) >) : ^a = 
    use e = source.GetEnumerator() 
    let mutable acc = LanguagePrimitives.GenericZero< (^a) >
    while e.MoveNext() do
        acc <- Checked.(+) acc e.Current
    acc

On the other hand, F# compiler builds a table to lookup zero values of all built-in numeric types before querying Zero members. The relevant bits are in this line and the code fragment below.

    type GenericZeroDynamicImplTable<'T>() = 
        static let result : 'T = 
            // The dynamic implementation
            let aty = typeof<'T>
            if   aty.Equals(typeof<sbyte>)      then unboxPrim<'T> (box 0y)
            elif aty.Equals(typeof<int16>)      then unboxPrim<'T> (box 0s)
            elif aty.Equals(typeof<int32>)      then unboxPrim<'T> (box 0)
            elif aty.Equals(typeof<int64>)      then unboxPrim<'T> (box 0L)
            elif aty.Equals(typeof<nativeint>)  then unboxPrim<'T> (box 0n)
            elif aty.Equals(typeof<byte>)       then unboxPrim<'T> (box 0uy)
            elif aty.Equals(typeof<uint16>)     then unboxPrim<'T> (box 0us)
            elif aty.Equals(typeof<uint32>)     then unboxPrim<'T> (box 0u)
            elif aty.Equals(typeof<uint64>)     then unboxPrim<'T> (box 0UL)
            elif aty.Equals(typeof<unativeint>) then unboxPrim<'T> (box 0un)
            elif aty.Equals(typeof<decimal>)    then unboxPrim<'T> (box 0M)
            elif aty.Equals(typeof<float>)      then unboxPrim<'T> (box 0.0)
            elif aty.Equals(typeof<float32>)    then unboxPrim<'T> (box 0.0f)
            else 
               let pinfo = aty.GetProperty("Zero")
               unboxPrim<'T> (pinfo.GetValue(null,null))
        static member Result : 'T = result

That said, if you would like to use List.sum on user-defined types, you need to define Zero member explicitly. Note that Zero doesn't make much sense in case of string type.

OTHER TIPS

Generally speaking, the F# specification is the best place to look for this sort of information. I believe that this should be covered in section 14.5.4.1 (Simulation of Solutions for Member Constraints), but it looks like Zero isn't actually mentioned there, which is almost certainly a spec bug.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top