Поливариадная обобщенная сумма
Этот ответ демонстрирует поливариадную функцию, которая суммирует свои аргументы:
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
Я создал обобщенную версию этой функции для всех членов Num
:
class (Num n) => MySumType n r where
mySum :: n -> r
instance (Num n) => MySumType n n where
mySum x = x
instance (Num n, MySumType n r) => MySumType n (n->r) where
mySum x = mySum . (x +)
Однако это работает только с такими звонками, как mySum (1::Int) (3::Int) (2::Int) :: Int
, Без спецификатора типа в аргументах я получаю эту ошибку:
Нет экземпляра для (MySumType n0 Float), возникающего из-за использования mySum
Возможное исправление: добавьте объявление экземпляра для (MySumType n0 Float)
В выражении: mySum 1.0:: Float
В уравнении для `it': it = mySum 1.0:: Float
Что вызывает эту проблему и как я могу это исправить? Я подозреваю, что это как-то связано с типом целочисленных литералов, являющихся Num a => a
, Также есть ли эквивалентная функция вышеупомянутой, которая не зависит от расширений? Используемые выше - это несколько классов типов параметров и гибкие экземпляры.
1 ответ
Я до сих пор не встречал убедительного варианта использования поливариадных функций в haskell, который пока не может быть решен с помощью списка или подобной структуры данных. Так что, если вы думаете, что у вас есть что-то новое, по той причине, по которой я играл с ними, я бы с удовольствием узнал, что это такое. Ниже приведено достаточно примеров, о некоторых из которых я должен был подумать, комментируя, что я отказался от своего заявления.
{-# language MultiParamTypeClasses #-}
{-# language FlexibleInstances #-}
{-# language TypeFamilies #-}
{-# language IncoherentInstances #-}
class (Num n) => MySumType n r where
mySum :: n -> r
instance (Num n, m~n) => MySumType n m where
mySum x = x
instance (Num n, MySumType n r, n~m) => MySumType n (m->r) where
mySum x = mySum . (x +)
Затем после загрузки файла в ghci:
> mySum 1 2 4 5 6 7 :: Int
25
> mySum 1.1 2 4.6 5 6.9 7 :: Double
26.6
Вывод типа также может быть вашим другом в некоторых случаях, позволяя вам отбрасывать окончательную аннотацию типа, как в следующем надуманном случае:
> replicate (mySum 1 2 3 4) 6
[6,6,6,6,6,6,6,6,6,6]
Что касается:
Также есть ли эквивалентная функция вышеупомянутой, которая не зависит от расширений?
Я думаю, что вам не повезло. Я хотел бы отметить, что если у вас нет причин уходить из GHC или оставаться на Haskell98 или Haskell2010, расширения не причинят вам вреда и не вызовут особых проблем с совместимостью, так как большинство людей в любом случае, похоже, находятся на GHC.
Изменить: Дополнительное объяснение
Давайте начнем с объяснения различия между более простыми экземплярами. Я добавлю постфикс 2 к некоторым именам для реализации, которую я предоставил.
instance (Num n) => MySumType n n where
mySum x = x
Если вы объедините это с объявлением класса:
class (Num n) => MySumType n r where
mySum :: n -> r
mySum
имеет тип подписи mySum :: (Num n) => n -> n
, Тот n -> n
говорит, что 1 функция арности, которая принимает тип n
, производит n
а также n
имеет класс Num.
При использовании этого mySum
Я должен указать, что я даю и что он производит.
mySum 1 :: Int
mySum (1 :: Int)
оба будут выдавать ошибки только при указании типа ввода и вывода, это даст результат:
mySum (1 :: Int) :: Int
^ ^
| specify output type
specify input type
дает результат ( 1 в этом случае). Это потому, что вы дали экземпляр для n -> n
но кто-то может позже добавить экземпляр для n -> m
такие как Int -> Double
как следующее:
instance MySumType Int Double where
mySum x = 2 * fromIntegral x
> mySum (1::Int) :: Int
1
> mySum (1::Int) :: Double
2.0
Каждый из этих экземпляров соответствует очень узкой полосе всех возможных типов функций арности.
Теперь давайте посмотрим на модифицированную версию
instance (Num n, m~n) => MySumType n m where
mySum x = x
mySum
здесь есть тип подписи mySum :: (Num n, m~n) => n -> m
этот тип подписи говорит для всех 1 функции арности, которые принимают тип n
и тип продукции m
где n
имеет класс Num и m
равно n
, Обратите внимание, что это началось с соответствия всем 1 функциям арности, n -> m
, любой n
любому m
затем положите на него контранты.
Теперь можно сделать:
> mySum2 (1::Int)
1
> mySum2 1 :: Int
1
Как только вход указан, выход также указан. Другие объяснения 1, 2.
Это не мешает нам указывать более конкретные случаи, такие как Int -> Double
как и раньше:
instance MySumType Int Double where
mySum x = 2 * fromIntegral x
> mySum2 1 :: Int
1
> mySum2 1 :: Double
1.0
> mySum2 (1 :: Int) :: Double
2.0
Только последний mySum2
работает от Int -> Double
пример. Это свойство IncoherentInstances
и я думаю, что я оставлю это на другой вопрос stackru, чтобы ответить на роль, IncoherentInstances
играет.