Я наткнулся на некоторые раздражающие вещи. Я знаю, что haskell работает со слабой головой нормальной формы (WHNF), и я знаю, что это такое. Введите следующий код в ghci (я использую команду: sprint, которая, насколько мне известно, сокращает выражение до WHNF):
let intlist = [[1,2],[2,3]]
:sprint intlist
дает intlist = _
это имеет смысл для меня.
let stringlist = ["hi","there"]
:sprint stringlist
дает stringlist = [_,_]
Это уже меня смущает. Но потом:
let charlist = [['h','i'], ['t','h','e','r','e']]
:sprint charlist
удивительно дает charlist = ["hi","there"]
Насколько я понял Haskell, строки - это не что иное, как списки символов, что, кажется, подтверждается проверкой типов "hi" :: [Char]
и ['h','i'] :: [Char]
.
Я в замешательстве, потому что в моем понимании все три приведенных выше примера более или менее одинаковы (список списков) и поэтому должны сводиться к одному и тому же WHNF, а именно _. Что мне не хватает?
Спасибо
"bla"
и['b','l','a']
выйдет иначе."bla"
может быть перегружен, но['b','l','a']
известно, что этоString
/[Char]
?['b', 'l', 'a']
может быть перегружено , а также"bla"
перегружено, только если-XOverloadedStrings
включено.Ответы:
Обратите внимание, что
:sprint
это не уменьшает выражение до WHNF. Если бы это было так, то4
вместо этого было бы следующее_
:Скорее, он
:sprint
берет имя привязки, пересекает внутреннее представление значения привязки и показывает уже «вычисленные части» (т.е. части, которые являются конструкторами) при использовании_
в качестве заполнителя для неоцененных блоков (т. Е. Приостановленной ленивой функции). звонки). Если значение полностью не оценено, оценка не будет выполнена, даже для WHNF. (И если значение будет полностью оценено, вы получите это, а не только WHNF.)В ваших экспериментах вы наблюдаете сочетание полиморфных и мономорфных числовых типов, различных внутренних представлений для строковых литералов и явных списков символов и т. Д. В основном вы наблюдаете технические различия в том, как различные литеральные выражения компилируются в байт-код. Таким образом, интерпретация этих деталей реализации как имеющих отношение к WHNF может безнадежно запутать вас. Как правило, вы должны использовать только
:sprint
как инструмент отладки, а не как способ узнать о WHNF и семантике оценки Haskell.Если вы действительно хотите понять, что
:sprint
происходит, вы можете включить несколько флагов в GHCi, чтобы увидеть, как на самом деле обрабатываются выражения и, таким образом, в конечном итоге компилируется в байт-код:После этого мы можем увидеть причину, по которой вы
intlist
даете_
:Вы можете игнорировать
returnIO
внешний и внешний:
вызов и сосредоточиться на той части, которая начинается с((\ @ a $dNum -> ...
Вот
$dNum
словарь дляNum
ограничения. Это означает, что сгенерированный код еще не разрешил фактический типa
в типеNum a => [[a]]
, поэтому все выражение по-прежнему представляется как вызов функции, принимающей (словарь для) соответствующийNum
тип. Другими словами, это неоцененный отрывок, и мы получаем:С другой стороны, укажите тип as
Int
, и код будет совершенно другим:и так
:sprint
вывод:Аналогично, буквенные строки и явные списки символов имеют совершенно разные представления:
и различия в
:sprint
выходных данных представляют собой артефакты, которые части выражения GHCi считает оцененными (явные:
конструкторы) по сравнению с неоцененными (unpackCString#
thunks).источник