Понимание ошибок ограничения значения F #

Я не понимаю, как работает Value Restriction в F #. Я прочитал объяснение в wiki, а также Документация MSDN. Я не понимаю, что:

  • Почему, например, это дает мне ошибку с ограничением значения (взято из этого вопроса):

    let toleq (e:float<_>) a b = (abs ( a - b ) ) < e
    

    Но ths не делает:

    let toleq e (a:float<_>) b = (abs ( a - b ) ) < e
    
  • Это обобщается правильно...

    let is_bigger a b = a < b
    

    но это не так (это указано как int):

    let add a b = a + b
    
  • Почему функции с неявными параметрами генерируют ограничение значения:

    let item_count = List.fold (fun acc _ -> 1 + acc) 0
    

    vs this:

    let item_count l = List.fold (fun acc _ -> 1 + acc) 0 l
    

    (Имейте в виду, что если я использую эту функцию в фрагменте кода, ошибка VR исчезнет, ​​но тогда функция будет указана для типа, в котором я его использовал, и я хочу, чтобы она была обобщена)

Как это работает?

(Я использую последние F #, v1.9.6.16)

Ответ 1

ИЗМЕНИТЬ

Лучшая/последняя информация находится здесь: Сохранение частично примененной общей функции

(оригинал ниже)

Я думаю, что прагматичная вещь здесь - не пытаться понять это слишком глубоко, а скорее знать пару общих стратегий, чтобы пройти мимо VR и продолжить работу. Это немного ответ "cop out", но я не уверен, что имеет смысл тратить время на понимание внутриутробных систем типа F # (которые по-прежнему меняются незначительными способами от выпуска до выпуска).

Две основные стратегии, которые я бы отстаивал, - это. Во-первых, если вы определяете значение с помощью типа функции (тип со стрелкой "- > " ), тогда убедитесь, что это синтаксическая функция, выполнив eta конверсия:

// function that looks like a value, problem
let tupleList = List.map (fun x -> x,x)
// make it a syntactic function by adding argument to both sides
let tupleList l = List.map (fun x -> x,x) l

Во-вторых, если вы все еще сталкиваетесь с проблемами VR/generalizing, тогда укажите полную подпись типа, чтобы сказать, что вы хотите (а затем "отступить", как позволяет F #):

// below has a problem...
let toleq (e:float<_>) a b = (abs ( a - b ) ) < e
// so be fully explicit, get it working...
let toleq<[<Measure>]'u> (e:float<'u>) (a:float<'u>) (b:float<'u>) : bool = 
    (abs ( a - b ) ) < e
// then can experiment with removing annotations one-by-one...
let toleq<[<Measure>]'u> e (a:float<'u>) b = (abs ( a - b ) ) < e

Я думаю, что эти две стратегии - лучший прагматичный совет. Тем не менее, здесь моя попытка ответить на ваши конкретные вопросы.

  • Я не знаю.

  • ' > ' - это полностью общая функция ('a → ' a → bool), которая работает для всех типов, и, таким образом, is_bigger обобщает. С другой стороны, "+" является "встроенной" функцией, которая работает на нескольких примитивных типах и определенном классе других типов; он может быть обобщен только внутри других "встроенных" функций, иначе он должен быть привязан к определенному типу (или будет по умолчанию "int" ). (Метод "inline" ad-hoc-полиморфизма - это то, как математические операторы в F # преодолевают отсутствие "классов типов".)

  • Это проблема "синтаксической функции", о которой я говорил выше; 'скомпилировать в поля/свойства, которые, в отличие от функций, не могут быть общими. Поэтому, если вы хотите, чтобы он был общим, сделайте его функцией. (См. Также этот вопрос для другого исключения из этого правила.)

Ответ 2

Было введено ограничение значения для решения некоторых проблем с полиморфизмом при наличии побочных эффектов. F # наследует это от OCaml, и я считаю, что ограничение стоимости существует во всех вариантах ML. Здесь несколько больше ссылки для чтения, помимо ссылок, которые вы указали. Поскольку Haskell является чистым, он не подвергается этому ограничению.

Что касается ваших вопросов, я думаю, что вопрос 3 действительно связан с ограничением стоимости, а первые два - нет.

Ответ 3

Никто, включая людей в команде F #, не знает ответа на этот вопрос каким-либо значимым образом.

Система вывода типа F # точно такая же, как грамматика VB6 в том смысле, что компилятор определяет правду.

Несчастливо, но верно.