试图找出Haskell类型类。为什么以下不起作用?
{-# LANGUAGE FlexibleInstances #-}
class IntClass t
instance IntClass Int
intToIntClass :: (IntClass r) => Int -> r
intToIntClass x = x
显然,“实例”并不意味着我认为它应该意味着什么。相反,它会给出错误消息,我不明白。
Could not deduce (r ~ Int)
from the context (IntClass r)
bound by the type signature for intToIntClass :: IntClass r => Int -> r
at f.hs:10:1-16
`r' is a rigid type variable bound by
the type signature for intToIntClass :: IntClass r => Int -> r
at f.hs:10:1
In the expression: x
In an equation for `intToIntClass t': intToIntClass x = x
答案 0 :(得分:14)
签名
intToIntClass :: (IntClass r) => Int -> r
表示只要该类型属于intToIntClass
,IntClass
就可以生成调用者想要的任何类型的值。但实现只能生成Int
个值。
到目前为止,Int
是唯一的实例是没有帮助的,就编译器而言,可能在其他模块中定义了更多的实例,因此编译器可以' t接受只生成Int
s。
答案 1 :(得分:9)
你写的这个函数说:“给我一些东西,我会把它还给你。”您写的类型说:“我可以将Int
转换为调用者想要的任何类型,只要该类型是IntClass
类型类的实例。”
当我们将它们组合在一起时,我们有一个函数将从调用者那里获取Int
,然后直接返回。除非返回非常相同的Int
,否则只要该类型是IntClass
类型类的成员,它就是调用者想要的任何类型的值。如何将输入从Int
变为调用者想要的任何(约束)类型?它不能。由于输入是直接返回的,因此返回类型必须与参数类型相同。类型检查器会从您的代码中推断出这一点,但是无法将该推断与您命名输出类型r
进行协调。类型检查器希望与您合作,但不能确定r
与Int
是同一的,因为r
是IntClass
的一个实例的唯一假设1}}类型类。
在fromInteger
类型类中找到的函数与您编写的函数Num
相似。如果你想谈论你可以基于Int
构建的所有类型,那么这应该是你的类型类的方法。类似的东西:
class IntClass a where
intToIntClass :: Int -> a
您可以为要成为IntClass
实例的每种类型定义此方法,并且具有所需类型的intToIntClass :: IntClass r => Int -> r
。此返回类型多态性主要取决于具有intToIntClass
的适当定义的每个参与类型。
答案 2 :(得分:4)
你的类型类很好。例如,这有效:
intId :: IntClass r => r -> r
intId = id
问题是,intToIntClass
的类型签名表示它从Int
映射到 {em} 成员IntClass
。编译器无法证明Int
是IntClass
的唯一成员,因此intToIntClass
的主体刚刚返回Int
,所以不够多态。
答案 3 :(得分:3)
为了进一步澄清,我们假设我将您的模块与新实例一起使用:
instance IntClass Integer
然后,intToIntClass
的实现也承诺类型为(IntClass Integer => Int -> Integer)
。但是你的实现与此相冲突。编译器假定类型类是“开放的”,意思是
因此,编译器看到今天唯一的实例是Int
,但它不能假设将来会保持这种情况。
(1.)和(2.)的组合使得对Haskell代码的思考和推理变得更加容易。当你知道今天的新实例无法搞砸昨天的代码并且今天的代码从明天的新实例中获得安全时,有更少的警告。