你在那里写的看起来很像一个类声明,而不是一个实例。也许你是这个意思?
class Printable n where
print :: n -> IO ()
read :: String -> n
请注意,n
必须是小写的,因为这是您要量化类的类型变量。如果你真的想定义一个实例,那么你,好吧,实例 n
化N
:
instance Printable N where
print n = ...
read str = ...
此时类型签名都是固定的(来自类定义),您需要编写的是这些函数的实际绑定,因此它必须是=
,而不是::
。
问题是:为什么你仍然需要自己的课程?它只会导致与标准函数的名称冲突,print
并且read
已经在前奏中。你实际上应该做的是用你的类型实例化那些标准类N
,即
instance Show N where
show n = ...
instance Read N where
readsPrec _ str = ...
也就是说,要回答您提出的实际问题:不,不可能为像. 编译器应该如何将其与更具体的类型或更一般的类型区分开来?这是相当绝望的。正确的做法是将它包装在一个新类型中;这隐藏了通用量化并允许编译器正确区分其他类型。∀ n . (n->n) -> n->n
(Int->Int) -> Int->Int
∀ n m . (n->m) -> n->m
N
或者,您可以只编写直接接受/产生的单态函数:N
showChurch :: N -> String
showChurch n = ...
readsPrecChurch :: Int -> ReadS N
readsPrecChurch _ str = ...
ReadS N
实际上后一个对于类型系统来说已经太多了:
readsPrecChurch :: Int -> String -> [(∀ n . (n->n) -> n->n, String)]
列表中的通用量化?哦哦。那是一种含蓄的类型。GHC 确实有一个-XImpredicativeTypes
扩展,但它并没有真正起作用。
同样,通过不公开使用多态类型来避免这些问题。Rank-N 类型(尤其是镜头)有一些很好的用途,但大多数时候它们完全是多余的和不必要的。肯定没有充分的理由实际使用这样的教堂数字。
newtype N = Church { getChurch :: ∀ n . (n->n) -> n->n }
将允许您毫无问题地定义任意实例或函数。而且,实际上,只是做
type N = Int
对于整数的所有标准实例当然也一样好......