Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What/where is get_Zero in F#'s int?

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").

like image 855
Danny Tuppeny Avatar asked Jun 22 '13 13:06

Danny Tuppeny


2 Answers

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.

like image 83
pad Avatar answered Nov 10 '22 23:11

pad


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.

like image 44
kvb Avatar answered Nov 11 '22 00:11

kvb