Haskell,多变量函数和类型推断

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)

我只是改变IntegerInt,变成了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考虑到我们IntSumRes类型类中使用Haskell为什么不能推断我们想要这种情况?

chi*_*chi 5

实例

instance (...) => SumRes (Int -> r) where
Run Code Online (Sandbox Code Playgroud)

大概的意思是"这里是如何定义SumResInt -> r任何r(在一定条件下)".比较它

instance (...) => SumRes (a -> r) where
Run Code Online (Sandbox Code Playgroud)

这意味着"这里是如何定义SumResa -> 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.

  • @FtheBuilder除了一些值得注意的例外,GHC默认尝试遵循[报告](https://www.haskell.org/onlinereport/haskell2010/),并且类型系列不在报告中.GHC可能是唯一支持它们的实现; 由于支持他们可能被视为一项重大的实施负担,我怀疑他们不会在不久的将来成为报告的一部分.但是,我不认为"TypeFamilies"是一个有争议或"icky"的延伸; 如果您喜欢它,并计划使用GHC,请使用它而不受惩罚. (2认同)