Суть в том, что с полиморфной xs
имеет тип формы
xs :: Num a => [a]
классы типов под капотом действительно только функции, они принимают дополнительный аргумент, который GHC автоматически заполняет, который содержит запись функции виджетов. Таким образом, вы можете думать о 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
Это именно такой путаницы в ограничение мономорфизма предназначено для предотвращения, поэтому вы должны * определенно * прочитать страницу [wiki на нем] (http://www.haskell.org/haskellwiki/Monomorphism_restriction). –