Ограничение значения при отсутствии общих параметров

Я получаю ошибку ограничения значения на let makeElem в следующем коде:

let elemCreator (doc: XmlDocument) = 
    fun name (value: obj) ->
        let elem = doc.CreateElement(name)
        match value with
        | :? seq<#XmlNode> as childs -> 
            childs |> Seq.iter (fun c -> elem.AppendChild(c) |> ignore)
            elem
        | _ -> elem.Value <- value.ToString(); elem

let doc = new XmlDocument()
let makeElem = elemCreator doc

Почему я получаю ошибку ограничения значения, если анонимная функция возвращается из elemCreator не имеет общих параметров?

Компилятор сообщает, что тип makeElem (string -> 'a -> XmlNode), Но почему это выводит второй параметр как 'a если я объявил это как obj?

3 ответа

Я полагаю, что это может быть "ожидаемым" поведением (хотя в данном случае и неудачным) в результате процессов обобщения и конденсации компилятора. Рассмотрим пример Томаса:

let foo (s:string) (a:obj) = a

Если бы вы должны были определить

let bar a = foo "test" a

тогда компилятор выведет тип bar : 'a -> obj потому что это обобщает тип первого аргумента. В вашем случае у вас есть эквивалент

let bar = foo "test"

так bar это значение, а не синтаксическая функция. Компилятор выполняет по существу ту же процедуру вывода, за исключением того, что теперь применяется ограничение значения. Это неудачно в вашем случае, так как это означает, что вы должны явно аннотировать makeElem с аннотацией типа (или сделать ее синтаксической функцией).

Это выглядит как неожиданное поведение для меня. Это можно продемонстрировать с помощью более простой функции:

let foo (s:string) (a:obj) = a
let bar = foo "bar"             // Value restriction

Одним из возможных объяснений может быть то, что компилятор F# позволяет вам вызывать функцию, принимающую параметр некоторого типа с аргументом любого подтипа. Итак, вы можете позвонить foo "hi" (new A()) без явного приведения A в obj (что раньше требовалось).

Это неявное приведение может означать, что компилятор фактически интерпретирует bar как то так:

let bar a = foo "bar" (a :> obj)

... и поэтому он считает, что аргумент является общим. В любом случае, это всего лишь предположение, поэтому вы можете попробовать отправить это в виде отчета об ошибке в fsbugs на microsoft dot com.

(Следующее основано исключительно на наблюдении.)

Если у вас есть функция obj -> 'a вызовы этой функции не используются для определения / определения типа ее аргумента. Иллюстрация:

let writeLine (arg: obj) = System.Console.WriteLine(arg)

writeLine является obj -> unit

let square x = 
  writeLine x
  x * x

В вышеуказанной функции x выводится как int потому что (*), Если тип может быть ограничен obj тогда эта функция не будет работать (x будет выводиться как obj до использования (*), что привело бы к ошибке по типу: type obj не поддерживает оператор (*)).

Я думаю, что это хорошее дело. Там нет необходимости ограничивать тип как obj потому что каждый тип уже неявно преобразуется в obj, Это позволяет вашей программе быть более универсальной и обеспечивает лучшую совместимость с.NET BCL.

Короче, obj не имеет никакого отношения к выводу типа (ууу!).

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