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").
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With