Поливариадная функция Хаскеля без аргументов
Я пытаюсь создать поливариадную функцию в 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
Но проблема в том, что когда функция вызывается без аргументов, она не работает.
Couldn't match expected type 'Integer' with actual type 'Integer -> r0'
Probable cause: 'sumOf' is applied to too few arguments
Например, я хотел бы иметь возможность написать sumOf :: Integer
и вернуть эту функцию 0
,
Как мне это сделать?
1 ответ
Самая простая версия работает только для Integer
Результаты.
Легкий способ
Это работает от того, что вы уже написали, используя тот факт, что 0
это личность для сложения.
class SumRes r where
sumOf' :: Integer -> r
instance SumRes Integer where
sumOf' = toInteger
instance (Integral b, SumRes r) => SumRes (b -> r) where
sumOf' a b = sumOf' $! a + toInteger b
sumOf :: SumRes r => r
sumOf = sumOf' 0
Два случая, Integer
а также b -> r
по своей сути не перекрываются.
Труднее путь
Чтобы получить более общие типы результатов, вам нужен несколько иной подход, потому что два описанных выше примера вместе Integer
заменяется переменной типа. Вы можете сделать это с MultiParamTypeClasses
а также TypeFamilies
,
{-# LANGUAGE ScopedTypeVariables, AllowAmbiguousTypes, DataKinds,
KindSignatures, TypeApplications, MultiParamTypeClasses,
TypeFamilies, FlexibleInstances #-}
module SumRes2 where
data Nat = Z | S Nat
class SumRes (c :: Nat) r where
sumOf' :: Integer -> r
type family CountArgs a :: Nat where
CountArgs (_ -> r) = 'S (CountArgs r)
CountArgs _ = 'Z
instance Num r => SumRes 'Z r where
sumOf' = fromInteger
instance (Integral b, SumRes n r) => SumRes ('S n) (b -> r) where
sumOf' a b = sumOf' @n (a + toInteger b)
sumOf :: forall r n. (SumRes n r, CountArgs r ~ n) => r
sumOf = sumOf' @n 0
Единственным ограничением является то, что если у вас есть Integral
экземпляр для типа функции, вы не можете использовать sumOf
производить это. Это не должно быть проблемой, хотя. Я использовал TypeApplications
а также AllowAmbiguousTypes
для краткости, но вы, конечно, можете использовать передачу по доверенности или Tagged
вместо.