Как сказал Дон Стюарт, классы типов в GHC реализованы с использованием «словарей».Это означает, что класс типов Num
представлен как запись функций (здесь я собираюсь пропустить ограничения Eq
и Show
):
class Num a where
fromInteger :: Integer -> a
...
становится
data Num a = Num { fromInteger :: Integer -> a, ... }
Когда вы определяете экземпляр, создается новая запись с функциями из этого экземпляра:
instance Num Integer where
fromInteger = id
...
становится
numDictForInteger = Num { fromInteger = id, ... }
Теперь, когда выиспользуйте эту функцию в полиморфном контексте, компилятор не знает, какой словарь использовать, поэтому он генерирует для него дополнительный параметр:
foo :: Num a => a
foo = 1
становится
foo :: Num a -> a
foo numDict = fromInteger numDict 1
Обратите внимание, какограничение Num a =>
становится параметром Num a ->
.
Однако, когда вы удаляете полиморфизм, компилятор знает, какой словарь использовать статически, поэтому он продолжает и вставляет его вместо генерации параметра:
foo :: Integer
foo = 1
становится
foo :: Integer
foo = fromInteger numDictForInteger 1
В качестве сноски, поэтому существует ограничение мономорфизма.Полиморфное значение не будет CAF, так как оно требует аргумента словаря.Это может привести к значительным отличиям характеристик производительности от ожидаемых, и поэтому вы вынуждены явно заявить, что это именно то, что вам нужно.