Что / где находится get_Zero в F# int?

Я только изучаю F#, и во время игры на http://www.tryfsharp.org/ я заметил, что если я изменю этот код:

[0..100]
|> List.sum

в

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

Я получаю следующую ошибку:

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

( Вот скрипт, который вы можете запустить / изменить в своем браузере, хотя он мне кажется, что он работает только в IE!)

Когда я проверил определение List.sum; он говорит, что тип должен иметь статический член с именем Zero. Это, кажется, объясняет ошибку; за исключением того факта, что я не вижу ни одного члена с именем Zero на int!

Так; где этот нулевой член, который относится к целым? Я не могу видеть это в intellisense, если я печатаю int.ни в документах, где говорится, что int - это просто.NET System.Int32 (который , похоже, не обладает статическим нулевым свойством).

(примечание: в сообщении об ошибке говорится "оператор", а не "участник"; это может быть связано; хотя определение List.sum просто говорит "участник").

2 ответа

Решение

Копаясь в исходном коде F#, List.sum (и Seq.sum) использует 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

С другой стороны, F# компилятор создает таблицу для поиска нулевых значений всех встроенных числовых типов перед запросом нулевых членов. Соответствующие биты находятся в этой строке, а фрагмент кода ниже.

    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

Тем не менее, если вы хотели бы использовать List.sum на пользовательских типах вам нужно явно определить нулевой элемент. Обратите внимание, что Zero не имеет особого смысла в случае строкового типа.

Вообще говоря, спецификация F# - лучшее место для поиска такого рода информации. Я считаю, что это должно быть рассмотрено в разделе 14.5.4.1 (Моделирование решений для ограничений членов), но похоже, что Zero там на самом деле не упоминается, что почти наверняка является ошибкой спецификации.

Другие вопросы по тегам