在寻找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
?
答案 0 :(得分:5)
实例
instance (...) => SumRes (Int -> r) where
粗略地表示“这里是SumRes
Int -> r
对r
{在某些条件下'(在某些条件下)”的定义。将其与
instance (...) => SumRes (a -> r) where
这意味着“以下是SumRes
a -> r
对a,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 => t
与Int
兼容,因为我们有SumRes Int
个实例,但如果我们没有明确指定Int
,那么我们没有足够的实例在这里申请,因为没有一般的SumRes t
实例。