为什么我可以直接在REPL中从类型类实例中调用函数(例如与Ord进行比较)?

evi*_*ing 7 haskell

当我在像带有Prelude的GHCI这样的REPL中时,我写

*> compare 5 7
LT
Run Code Online (Sandbox Code Playgroud)

为什么我compare可以在REPL中直接调用该函数()?

我知道compare是在typeclass中定义的Ord。类型类的定义Ord当然表明它是的子类Eq

这是我的推理路线: 5具有type Num a => a,并且Numtypeclass不是的子类Eq。也,

Prelude> :t (compare 5)
(compare 5) :: (Num a, Ord a) => a -> Ordering
Run Code Online (Sandbox Code Playgroud)

因此,当我应用数字类型参数时,这里还有一个附加的约束。当我打电话时compare 5 7,参数的类型缩小到确实有一个实例的东西Ord。我认为范围的缩小发生在与typeclass相关联的默认具体类型上:在的情况下Num,这是Integer,其实例为Real,其实例为Ord

但是,由于来自非功能性编程背景,我会想象必须调用compare其中一个数字(例如,在OOP中的对象上对其进行调用)。如果5is是Integer实现的Ord,那为什么我要调用compareREPL本身呢?对于我来说,这显然是一个与范式转换有关的问题,但我仍然没有理解。希望有人可以解释。

Wil*_*sem 11

类型违约这里的用武之地。解释器可以推导出57需要是相同类型的,并且所述的构件OrdNum类型类。a的默认值NumInteger,并且由于Integer也是的实例,Ord因此我们可以使用Integer

因此5,在这种情况下,解释器考虑并7Integer此处,因此它可以评估功能并获得LT

GHCi还有一些其他的默认规则,如GHCi文档中所述


mel*_*ene 9

诸如此类compare的方法与类型相关联,而不与特定值相关联。编译器需要能够推断出类型,以便选择正确的typeclass实例,但这不需要任何特殊帮助。

的类型compare

compare :: (Ord a) => a -> a -> Ordering
Run Code Online (Sandbox Code Playgroud)

因此,其任何参数(类型为a)都可以用于查找Ord实例。

如您所正确假设的那样,在compare 5 7示例中,类型为57默认为Integer。因此acompare推导类型为,IntegerOrd Integer选择实例。

该选择不一定要通过函数参数。考虑例如

read :: (Read a) => String -> a
Run Code Online (Sandbox Code Playgroud)

这是驱动实例选择的结果类型,但是类型检查器就可以了:

> read "(2, 3)" :: (Int, Int)
(2,3)
Run Code Online (Sandbox Code Playgroud)

(相当于OO的是什么"(2, 3)".read()??)

实际上,方法甚至不必一定是函数:

maxBound :: (Bounded a) => a
Run Code Online (Sandbox Code Playgroud)

这是一个多态值,而不是一个函数:

> maxBound :: Int
9223372036854775807
Run Code Online (Sandbox Code Playgroud)

类实例是唯一地与类型相关联的,因此,只要类型检查器具有足够的信息来找出该类型变量所代表的内容,一切都会正常进行。也就是说,在

someMethod :: (SomeClass foo) => ...
Run Code Online (Sandbox Code Playgroud)

foo必须在类型签名中的某个位置出现,...以便类型检查器可以解决在任何给定点使用SomeClass foo的方式someMethod(至少在没有某些语言扩展的情况下)。