首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Haskell,多元函数与类型推理

Haskell,多元函数与类型推理
EN

Stack Overflow用户
提问于 2015-10-25 18:49:53
回答 2查看 254关注 0票数 3

在查找Polyvariadic函数示例时,我找到了以下资源:StackOverflow: How to create a polyvariadic haskell function?,其中有一个回答片段,如下所示:

代码语言:javascript
复制
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

然后我们可以利用:

代码语言:javascript
复制
*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

为了好奇,我试着改变一下它,因为我第一眼就发现它很棘手,于是我就进入了这个过程:

代码语言:javascript
复制
class SumRes r where
  sumOf :: Int -> r

instance SumRes Int where
  sumOf = id

instance (SumRes r) => SumRes (Int -> r) where
  sumOf x = sumOf . (x +)

我刚刚将Integer改为Int,并将instance (Integral a, SumRes r) => SumRes (a -> r) where的多态性改为instance (SumRes r) => SumRes (Int -> r) where

要编译它,我必须设置XFlexibleInstances标志。当我尝试测试sumOf函数时,我遇到了一个问题:

代码语言:javascript
复制
*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...

然后我试着:

代码语言:javascript
复制
*Main> sumOf (1 :: Int) (1 :: Int) :: Int
2

为什么Haskell不能推断在这种情况下我们需要一个Int,考虑到我们在SumRes类型中使用的是Int

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2015-10-25 19:55:52

实例

代码语言:javascript
复制
instance (...) => SumRes (Int -> r) where

大致意思是“这里是如何为任何SumRes (在特定条件下)在Int -> r上定义r”。把它和

代码语言:javascript
复制
instance (...) => SumRes (a -> r) where

这意味着“这里是如何为任何SumRes (在特定条件下)在a -> r上定义a,r”。

主要的区别是,第二个实例声明这是相关的实例,无论a,r类型是什么。除了一些(非常棘手和潜在危险的) Haskell扩展,您不能在以后添加更多涉及函数的实例。相反,第一个例子为新的实例留出了空间,例如。

代码语言:javascript
复制
instance (...) => SumRes (Double -> r) where ...
instance (...) => SumRes (Integer -> r) where ...
instance (...) => SumRes (Float -> r) where ...
instance (...) => SumRes (String -> r) where ... -- nonsense, but allowed

这与数字文字(如5 )是多态的事实相匹配:它们的类型必须从上下文中推断出来。因为编译器稍后可能会找到一个Double -> r实例,并选择Double作为文本类型,所以编译器不会提交到Int -> r实例,并报告类型错误中的歧义。

请注意,使用一些(安全的) Haskell扩展(如TypeFamilies),可以向编译器“承诺”您的Int -> r是整个程序中唯一声明的扩展。这是这样做的:

代码语言:javascript
复制
instance (..., a ~ Int) => SumRes (a -> r) where ...

这将保证处理所有"functional“情况,但要求a实际上是与Int相同的类型。

票数 5
EN

Stack Overflow用户

发布于 2015-10-25 19:14:14

数字文字本身是多态的,而不是Int类型的。

代码语言:javascript
复制
*Main> :t 1
1 :: Num a => a

看看当我们得到类型签名时会发生什么:

代码语言:javascript
复制
*Main> :t sumOf 1 2 3
sumOf 1 2 3 :: (Num a, Num a1, SumRes (a -> a1 -> t)) => t

注意,该类型根本没有提到Int。类型检查器无法确定如何实际计算和,因为定义的Int实例中没有一个能够在这里应用。

如果您将类型修复为Int,那么您的结果是

代码语言:javascript
复制
*Main> :t sumOf (1 :: Int) (2 :: Int) (3 :: Int)
sumOf (1 :: Int) (2 :: Int) (3 :: Int) :: SumRes t => t

*Main> :t sumOf (1 :: Int) (2 :: Int) (3 :: Int) :: Int
sumOf (1 :: Int) (2 :: Int) (3 :: Int) :: Int

注意,SumRes t => tInt兼容,因为我们有一个SumRes Int实例,但是如果我们没有显式指定Int,那么我们就没有足够通用的实例可以在这里应用,因为没有通用的SumRes t实例。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/33333614

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档