: спринт для полиморфных значений?
Мне интересно, почему :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
является полиморфным в первом случае, но не во втором? Я хотел бы знать, что происходит внутри страны.
- Как я могу справиться со многими уровнями отступов?
- Чтение ядра GHC
- Функция Haskell типа: IO String-> String
- Конкретный пример, показывающий, что монады не закрыты по составу (с доказательством)?
- Вычисление вытравливания
- Инструменты для анализа производительности программы Haskell
- Haskell GHC: какова временная сложность совпадения шаблона с конструкторами N?
- Haskell: Что такое нормальная форма слабой головы?
- двойной stream для предотвращения ненужной memoization?
- Интегральные операторы quot vs. div
- Возможно ли программирование графического интерфейса?
- Haskell читает исходный ввод с клавиатуры
- Сравнение веб-фреймворков Haskell Snap и Yesod
Суть в том, что с полиморфным xs
он имеет вид формы
xs :: Num a => [a]
typeclasses под капотом – действительно просто функции, они берут дополнительный аргумент, который GHC автоматически заполняет, содержащий запись о функциях typeclasses. Таким образом, вы можете думать о xs
имеющем тип
xs :: NumDict a -> [a]
Поэтому, когда вы запускаете
Prelude> length xs
Он должен выбрать некоторое значение для a
и найти соответствующее значение NumDict
. IIRC он заполнит его Integer
, так что вы на самом деле вызываете функцию и проверяете длину результирующего списка.
Когда вы тогда :sprint
xs
, вы снова заполните этот аргумент, на этот раз со свежей переменной типа. Но дело в том, что вы получаете совершенно другой список, вы дали ему другой NumDict
поэтому он не был вынужден каким-либо образом, когда вы называли length
раньше.
Это сильно отличается от явно monoморфного списка, так как там действительно есть только один список, есть только одно значение, чтобы заставить так, когда вы вызываете длину, он заставляет его для всех будущих использования 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
Это действительно то, что типа classов, как под капотом, GHC будет автоматически заполнить этот первый Smash a
аргумент для нас. Теперь ваш первый пример подобен join
, мы не можем делать какие-либо предположения о том, какой результат будет, когда мы применяем его к разным типам, но ваш второй пример больше похож на
join' :: [Int] -> Int join' = join intSmash