Как работает оценка в Haskell для выражений с ограничениями - PullRequest
5 голосов
/ 01 апреля 2020

Предположим, я пишу в GHCi:

GHCi> let x = 1 + 2 :: Integer
GHCi> seq x ()
GHCi> :sprint x

GHCi печатает x = 3, как и ожидалось.

Однако

GHCi> let x = 1 + 2
GHCi> seq x ()
GHCi> :sprint x

дает x = _

Единственное различие между этими двумя выражениями заключается в их типах (Integer против Num a => a). У меня вопрос, что именно происходит, и почему, по-видимому, x не оценивается в последнем примере.

1 Ответ

3 голосов
/ 01 апреля 2020

Основная проблема в том, что

let x = 1 + 2

определяет polymorphi c значение типа forall a. Num a => a, и это то, что оценивается аналогично функции.

Каждое использование x может быть выполнено в другом типе, например x :: Int, x :: Integer, x :: Double и так далее. Эти результаты никоим образом не «кэшируются», а пересчитываются каждый раз, как если бы x была функцией, которая, так сказать, вызывается несколько раз.

Действительно, общая реализация классов типов реализует такие полиморф c x как функция

x :: NumDict a -> a

, где приведенный выше аргумент NumDict a автоматически добавляется компилятором и содержит информацию о том, что a является типом Num, включая как выполнить сложение, как интерпретировать целочисленные литералы внутри a и так далее. Это называется реализацией «передачи словаря».

Таким образом, использование polymorphi c x несколько раз действительно соответствует вызову функции несколько раз, что вызывает повторное вычисление. Чтобы избежать этого, (* страшное) ограничение мономорфизма было введено в Haskell, заставив x быть мономорфным c. MR не является идеальным решением и в некоторых случаях может привести к неожиданным ошибкам типа.

Чтобы решить эту проблему, MR отключен по умолчанию в GHCi, поскольку в GHCi нас не очень заботит производительность - удобство использования там важнее. Это, однако, вызывает повторное вычисление, как вы обнаружили.

...