: спринт для полиморфных значений?

Мне интересно, почему :sprint сообщает xs = _ в этом случае:

Prelude> xs = map (+1) [1..10]
Prelude> length xs
10
Prelude> :sprint xs
xs = _

но не в этом случае:

Prelude> xs = map (+1) [1..10] :: [Int]
Prelude> length xs
10
Prelude> :sprint xs
xs = [_,_,_,_,_,_,_,_,_,_]

Примечание. Я запускаю ghci с помощью -XNoMonomorphismRestriction. Имеет ли это отношение к тому, что тип xs является полиморфным в первом случае, но не во втором? Я хотел бы знать, что происходит внутри.

Ответ 1

Суть в том, что с полиморфным xs он имеет тип формы

xs :: Num a => [a]

typeclasses под капотом - это действительно просто функции, они берут дополнительный аргумент, который GHC автоматически заполняет, который содержит запись функций typeclasses. Итак, вы можете думать о xs, имеющем тип

xs :: NumDict a -> [a]

Итак, когда вы запустите

Prelude> length xs

Он должен выбрать некоторое значение для a и найти соответствующее значение NumDict. IIRC он заполнит его Integer, так что вы на самом деле вызываете функцию и проверяете длину результирующего списка.

Когда вы затем :sprint xs, вы снова заполните этот аргумент, на этот раз с новой переменной типа. Но дело в том, что вы получаете совершенно другой список, вы дали ему другой NumDict, чтобы он не был принудительно вызван, когда вы вызывали length раньше.

Это сильно отличается от явно мономорфного списка, так как на самом деле существует только один список, есть только одно значение, чтобы заставить так, когда вы вызываете длину, он заставляет его для всех будущих использования xs.

Чтобы сделать это немного яснее, рассмотрите код

data Smash a = Smash { smash :: a -> a -> a }
-- ^ Think of Monoids

intSmash :: Smash Int
intSmash = Smash (+)

listSmash :: Smash [a]
listPlus = Smash (++)

join :: Smash a -> [a] -> a
join (Smash s) xs = foldl1' s xs

Это действительно то, что типы классов похожи на капот, GHC автоматически заполнит этот первый аргумент Smash a для нас. Теперь ваш первый пример похож на join, мы не можем делать какие-либо предположения о том, что будет выводиться, когда мы применяем его к разным типам, но ваш второй пример больше похож на

join' :: [Int] -> Int
join' = join intSmash