最简单的版本只适用于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
and AllowAmbiguousTypes
,但您当然可以使用代理传递或Tagged
代替。