Haskell, поливариадная функция и вывод типа

При поиске примеров поливариадных функций я нашел этот ресурс: Stackru: как создать поливариадную функцию haskell? и был фрагмент ответа, подобный этому:

class SumRes r where 
  sumOf :: Integer -> r

instance SumRes Integer where
  sumOf = id

instance (Integral a, SumRes r) => SumRes (a -> r) where
  sumOf x = sumOf . (x +) . toInteger

Тогда мы могли бы использовать:

*Main> sumOf 1 :: Integer
1
*Main> sumOf 1 4 7 10 :: Integer
22
*Main> sumOf 1 4 7 10 0 0  :: Integer
22
*Main> sumOf 1 4 7 10 2 5 8 22 :: Integer
59

Я попытался немного его изменить, просто для любопытства, потому что на первый взгляд это показалось мне довольно сложным, и я понял это:

class SumRes r where
  sumOf :: Int -> r

instance SumRes Int where
  sumOf = id

instance (SumRes r) => SumRes (Int -> r) where
  sumOf x = sumOf . (x +)

Я только что изменился Integer в Int и повернулся instance (Integral a, SumRes r) => SumRes (a -> r) where менее полиморфный к instance (SumRes r) => SumRes (Int -> r) where

Чтобы скомпилировать это я должен был установить XFlexibleInstances флаг. Когда я попытался проверить sumOf Функция у меня возникла проблема:

*Main> sumOf 1 :: Int
1
*Main> sumOf 1 1 :: Int
<interactive>:9:9
    No instance for (Num a0) arising from the literal `1'
    The type variable `a0' is ambiguous...

Тогда я попробовал:

*Main> sumOf (1 :: Int) (1 :: Int) :: Int
2

Почему Хаскелл не может сделать вывод, что мы хотим Int в этой ситуации, учитывая, что мы используем Int в нашем SumRes класс типов?

2 ответа

Решение

Экземпляр

instance (...) => SumRes (Int -> r) where

примерно означает "вот как определить SumRes на Int -> r для любого r (при определенных условиях)". ​​Сравните это с

instance (...) => SumRes (a -> r) where

что означает "вот как определить SumRes на a -> r для любого a,r (при определенных условиях)".

Основным отличием является то, что второй утверждает, что это соответствующий экземпляр, какой бы тип a,r возможно. За исключением некоторого (очень хитрого и потенциально опасного) расширения Haskell, нельзя добавить больше экземпляров позже при включении функций. Вместо этого первый оставляет место для новых экземпляров, таких как, например,

instance (...) => SumRes (Double -> r) where ...
instance (...) => SumRes (Integer -> r) where ...
instance (...) => SumRes (Float -> r) where ...
instance (...) => SumRes (String -> r) where ... -- nonsense, but allowed

Это связано с тем, что числовые литералы, такие как 5 являются полиморфными: их тип должен быть выведен из контекста. Поскольку позже компилятор может найти, например, Double -> r экземпляр и выбрать Double как литеральные типы, компилятор не фиксирует Int -> r экземпляр, и сообщает о неоднозначности в ошибке типа.

Обратите внимание, что при использовании некоторых (безопасных) расширений Haskell (таких как TypeFamilies), можно "пообещать" компилятору, что ваш Int -> r это единственный, который будет объявлен во всей программе. Это сделано так:

instance (..., a ~ Int) => SumRes (a -> r) where ...

Это обещает обработать все случаи "функционального типа", но требует, чтобы a на самом деле тот же тип, что и Int,

Числовые литералы сами по себе полиморфны, а не имеют тип Int

*Main> :t 1
1 :: Num a => a

Посмотрите, что происходит, когда мы получаем сигнатуру типа:

*Main> :t sumOf 1 2 3
sumOf 1 2 3 :: (Num a, Num a1, SumRes (a -> a1 -> t)) => t

Обратите внимание, что тип не упоминает Int совсем. Средство проверки типов не может понять, как на самом деле вычислить сумму, потому что ни один из определенных Int примеры достаточно общие, чтобы подать заявку здесь.

Если вы исправите типы, которые будут Intтогда вы в конечном итоге

*Main> :t sumOf (1 :: Int) (2 :: Int) (3 :: Int)
sumOf (1 :: Int) (2 :: Int) (3 :: Int) :: SumRes t => t

*Main> :t sumOf (1 :: Int) (2 :: Int) (3 :: Int) :: Int
sumOf (1 :: Int) (2 :: Int) (3 :: Int) :: Int

Обратите внимание, что SumRes t => t совместим с Int потому что у нас есть SumRes Int экземпляр, но если мы не укажем явно Int, тогда у нас нет достаточно общих примеров, чтобы подать заявку здесь, потому что нет общего SumRes t пример.

Другие вопросы по тегам