Понимание ограничения значения F # - PullRequest
1 голос
/ 16 апреля 2020

Я учу F #. Я здесь, потому что у меня было что-то сложное для понимания ограничения стоимости.

Вот примеры из книги, с которой я учусь.

let mapFirst = List.map fst

С тех пор, как я изучил FP с haskell, я был почти уверен, что этот код будет хорошо скомпилирован, но это не так. Это привело к ошибке FS0030 (К сожалению, я не могу скопировать и вставить сообщение об ошибке fsi, так как оно было написано на корейском языке). Вместо этого мне пришлось предоставить явный аргумент вроде:

let mapFirst inp = List.map fst inp   // or inp |> List.map fst

Но почему? Я думал, что в приведенном выше примере компилятор может точно определить тип заданного значения:

val mapFirst : ('a * 'b) list -> 'a list

Если я правильно напомню, я назвал эту вещь в haskell Эта-преобразование , и выше два примера полностью идентичны. (Может быть, не полностью , хотя). Почему я должен явно указывать параметры, чтобы функция могла быть каррирована без потери информации?

Я понял, что что-то вроде

let empties = Array.create 100 []

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

※ Я посмотрел на этот вопрос , но это не помогло.

1 Ответ

4 голосов
/ 16 апреля 2020

Это связано с изменчивостью.

Рассмотрим этот фрагмент:

type T<'a> = { mutable x : 'a option }

let t = { x = None }

Тип t равен T<'a>, то есть t является общим c, он имеет универсальный c параметр 'a, означающий, что t.x может быть любого типа - независимо от того, что выберет потребитель.

Затем, предположим, в одной части программы, которую вы делаете:

t.x <- Some 42

Совершенно законно: при доступе к t вы выбираете 'a = int, а затем t.x : int option, так что вы можете вставить sh Some 42 в него.

Затем, предположим, в другой части вашей программы вы делаете:

t.x <- Some "foo"

О нет, что происходит сейчас? t.x : int option или string option? Если компилятор добросовестно скомпилирует ваш код, это приведет к повреждению данных. Так что компилятор отказывается, на всякий случай.

Так как в общем случае компилятор не может действительно проверить, есть ли что-то изменчивое глубоко внутри вашего типа, он принимает безопасный маршрут и отклоняет значения (что означает «не функции») предполагается, что они являются обобщенными c.


Обратите внимание, что это относится к syntacti c значениям, а не логическим . Даже если ваше значение действительно является функцией, но синтаксически не определяется как таковое (то есть не имеет параметров), ограничение по-прежнему применяется. В качестве иллюстрации рассмотрим следующее:

type T<'a> = { mutable x : 'a option }

let f t x = 
  t.x <- Some x

let g = f { x = None }

Здесь, хотя g действительно функция, ограничение работает точно так же, как в моем первом примере выше: каждый вызов g пытается работать с тем же родовым значением c значение T<'a>


В некоторых более простых случаях компилятор может использовать ярлык. Так, например, одна эта строка не компилируется:

let f = List.map id

Но эти две строки делают:

let f = List.map id
let x = f [1;2;3]

Это потому, что вторая строка позволяет компилятору выводить, что f : list int -> list int, поэтому параметр generi c исчезает, и все счастливы.

На практике оказывается, что этот ярлык охватывает подавляющее большинство случаев. Единственный раз, когда вы действительно сталкиваетесь с ограничением значения, это когда вы пытаетесь экспортировать такое значение generi c из модуля.


В Haskell вся эта ситуация не ' этого не происходит, потому что Haskell не допускает мутации. Все просто.

Но опять же, хотя Haskell не допускает мутации, это своего рода сорта - через unsafePerformIO. И угадайте, что - в этом сценарии вы рискуете столкнуться с той же проблемой. Это даже , упомянутое в документации .

За исключением GH C не отказывается от его компиляции - в конце концов, если вы используете unsafePerformIO, вы должны знать, что делаете. Правильно? : -)

...