Fth*_*der 3 recursion haskell typeclass polyvariadic
在寻找Polyvariadic函数示例时,我发现了这个资源: StackOverflow:如何创建polyvariadic 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
Run Code Online (Sandbox Code Playgroud)
然后我们可以使用:
*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
Run Code Online (Sandbox Code Playgroud)
为了好奇,我试着稍微改变一下,因为我发现它乍一看非常棘手,我进入了这个:
class SumRes r where
sumOf :: Int -> r
instance SumRes Int where
sumOf = id
instance (SumRes r) => SumRes (Int -> r) where
sumOf x = sumOf . (x +)
Run Code Online (Sandbox Code Playgroud)
我只是改变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...
Run Code Online (Sandbox Code Playgroud)
然后我尝试了:
*Main> sumOf (1 :: Int) (1 :: Int) :: Int
2
Run Code Online (Sandbox Code Playgroud)
Int考虑到我们Int在SumRes类型类中使用Haskell为什么不能推断我们想要这种情况?
实例
instance (...) => SumRes (Int -> r) where
Run Code Online (Sandbox Code Playgroud)
大概的意思是"这里是如何定义SumRes的Int -> r任何r(在一定条件下)".比较它
instance (...) => SumRes (a -> r) where
Run Code Online (Sandbox Code Playgroud)
这意味着"这里是如何定义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
Run Code Online (Sandbox Code Playgroud)
这与数字文字(例如5多态)这一事实配对:它们的类型必须从上下文中推断出来.由于稍后编译器可能会找到例如一个Double -> r实例并选择Double作为文字类型,因此编译器不会提交Int -> r实例,并报告类型错误中的歧义.
请注意,使用一些(安全的)Haskell扩展(例如TypeFamilies),可以向编译器"承诺"您Int -> r是唯一将在整个程序中声明的内容.这样做是这样的:
instance (..., a ~ Int) => SumRes (a -> r) where ...
Run Code Online (Sandbox Code Playgroud)
这承诺处理所有"功能类型"的情况,但要求a实际上是相同的类型Int.