当我在像带有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
,并且Num
typeclass不是的子类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中的对象上对其进行调用)。如果5
is是Integer
实现的Ord
,那为什么我要调用compare
REPL本身呢?对于我来说,这显然是一个与范式转换有关的问题,但我仍然没有理解。希望有人可以解释。
诸如此类compare
的方法与类型相关联,而不与特定值相关联。编译器需要能够推断出类型,以便选择正确的typeclass实例,但这不需要任何特殊帮助。
的类型compare
是
compare :: (Ord a) => a -> a -> Ordering
Run Code Online (Sandbox Code Playgroud)
因此,其任何参数(类型为a
)都可以用于查找Ord
实例。
如您所正确假设的那样,在compare 5 7
示例中,类型为5
,7
默认为Integer
。因此a
,compare
推导类型为,Integer
并Ord 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
(至少在没有某些语言扩展的情况下)。