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

时间:2015-10-25 18:49:53

标签: haskell recursion typeclass polyvariadic

在寻找Polyvariadic函数示例时,我发现了这个资源: StackOverflow: How to create a polyvariadic haskell function?,并且有一个这样的答案片段:

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

然后我们可以使用:

*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

我试着稍微改变一下,只是为了好奇,因为我乍一看发现它很棘手,我进入了这个:

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函数时,我遇到了一个问题:

*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...

然后我尝试了:

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

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

2 个答案:

答案 0 :(得分:5)

实例

instance (...) => SumRes (Int -> r) where

粗略地表示“这里是SumRes Int -> rr {在某些条件下'(在某些条件下)”的定义。将其与

进行比较
instance (...) => SumRes (a -> r) where

这意味着“以下是SumRes a -> ra,r a,r的定义instance (...) => SumRes (Double -> r) where ... instance (...) => SumRes (Integer -> r) where ... instance (...) => SumRes (Float -> r) where ... instance (...) => SumRes (String -> r) where ... -- nonsense, but allowed ”(在某些条件下)。

主要区别在于第二个表明这是 相关实例,无论5类型是什么。除非有一些(非常棘手且具有潜在危险性)的Haskell扩展,否则以后在涉及函数时无法添加更多实例。相反,第一个为新实例留出了空间,例如,例如

Double -> r

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

请注意,使用一些(安全的)Haskell扩展(例如instance (..., a ~ Int) => SumRes (a -> r) where ... ),可以向编译器“承诺”a是唯一将在整个计划。这是这样做的:

Int

这承诺处理所有“功能类型”案例,但要求library(ggplot2) library(scales) library(jpeg) library(scales) library(grid) #picture from internet myurl <- "http://upload.wikimedia.org/wikipedia/commons/9/95/Apollonian_spheres.jpg" z <- tempfile() download.file(myurl,z,mode="wb") pic <- readJPEG(z) file.remove(z) # cleanup x <- sample(1:10, replace=T, 10) y <- c("a","b","c","d","e","f","g","h","i", "j") df <- data.frame(y,x) p <-ggplot(df, aes(y, x, fill=y)) + annotation_custom(rasterGrob(pic, width=unit(1,"npc"), height=unit(1,"npc")), -Inf, Inf, -Inf, Inf) + geom_bar(stat = "identity", fill="red",width=0.8, alpha=0.75 )+ #geom_text(aes(label=data2$Attributes), vjust=1.5,colour="black") ggtitle("Something")+ theme_classic() + labs(y = "yyy", x = "xxx") + guides(fill = guide_legend(reverse=TRUE))+ theme(axis.text.y = element_blank()) + theme(plot.title = element_text(size=20))+ theme(axis.title.x = element_text(size = 16))+ theme(axis.title.y = element_text(size = 16))+ theme(legend.text = element_text( size = 14)) + theme(legend.title = element_text( size = 16))+ theme(panel.grid.major = element_line(colour = "white", linetype = "dotted")) p 实际上与RAILS_ENV=production bin/rake assets:precompile 类型相同。

答案 1 :(得分:3)

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

*Main> :t 1
1 :: Num a => a

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

*Main> :t sumOf 1 2 3
sumOf 1 2 3 :: (Num a, Num a1, SumRes (a -> a1 -> t)) => t

请注意,该类型根本没有提到Int。类型检查器无法弄清楚如何实际计算总和,因为定义的Int实例都不足以在此处应用。

如果您将类型修改为Int,那么您最终会使用

*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实例。