Haskell 不带参数的多元函数
Haskell polyvariadic function with no arguments
我正在尝试在 Haskell 中创建多元函数,我使用 this 答案来创建基本函数。
这是函数的代码:
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
并具有此功能return 0
。
我应该怎么做?
最简单的版本仅适用于 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
。
我正在尝试在 Haskell 中创建多元函数,我使用 this 答案来创建基本函数。 这是函数的代码:
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
并具有此功能return 0
。
我应该怎么做?
最简单的版本仅适用于 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
。