我正在编写一个库来处理使用延迟评估的无限序列。为简洁起见,我使用广义代数数据类型(GADT)来对序列中每个术语的索引断言Ord
约束。因此,以下类型检查:
{-# LANGUAGE GADTs #-}
data Term ix cff where
Term :: (Ord ix) => ix -> cff -> Term ix cff
data Sequence ix cff where
Seq :: [Term ix cff] -> Sequence ix cff
f (Seq (Term i x:_)) (Seq (Term j y:_)) = i < j
{-
add :: Sequence ix cff -> Sequence ix cff -> Sequence ix cff
add (Seq tms1) (Seq tms2) = Seq (addAlong (<) tms1 tms2)
where addAlong :: (ix -> ix -> Bool) ->
[Term ix cff] -> [Term ix cff] -> [Term ix cff]
addAlong ord (Term i x : tms1) (Term j y : tms2) = []
-}
正如所料,GHCi告诉我f :: Sequence t t1 -> Sequence t t2 -> Bool
。进行比较i < j
需要Ord
个实例,但(Ord ix)
定义中的约束Term
会对此进行处理。
但是,当取消注释下部块时,add
函数无法使用错误No instance for (Ord ix) arising from the use of ``<''
进行类型检查。它不应该能够从(Ord ix)
定义中出现的Term ix cff
中找出Sequence
吗?
答案 0 :(得分:6)
绑定到函数参数的术语默认是单态的(也就是rank-0多态),因为Haskell98只支持rank-1多态,而且多态参数使得函数rank-2是多态的。
因此,在Seq (addAlong (<) tms1 tms2)
中,编译器只能将<
视为刚性类型ix
上的单态比较。要将<
视为单态函数,编译器需要解析Ord
实例。但是,此时Ord ix
实例不可用,因为它只能与Term
匹配!
与原始代码最接近的解决方案是明确地使addAlong
rank-2多态:
{-# LANGUAGE RankNTypes, UnicodeSyntax #-}
add :: Sequence ix cff -> Sequence ix cff -> Sequence ix cff
add (Seq tms1) (Seq tms2) = Seq (addAlong (<) tms1 tms2)
where addAlong :: (∀ ix' . Ord ix' -> ix' -> Bool) ->
[Term ix cff] -> [Term ix cff] -> [Term ix cff]
addAlong ord (Term i x : tms1) (Term j y : tms2) = []
这样,<
只是按原样传递(作为多态Ord => ...
方法),因此编译器不需要在Seq (addAlong (<) tms1 tms2)
处提供实例,但是如果您有Term
可用,可以稍后解决。
你当然应该考虑你是否真的需要它。在每个Ord
中保留一个Term
词典对我来说似乎相当浪费 - 如果你在约Seq
中保留约束,那么问题就不会出现:
data Term ix cff where Term :: ix -> cff -> Term ix cff
data Sequence ix cff where
Seq :: Ord ix => [Term ix cff] -> Sequence ix cff
add :: Sequence ix cff -> Sequence ix cff -> Sequence ix cff
add (Seq tms1) (Seq tms2) = Seq (addAlong (<) tms1 tms2)
where addAlong :: (ix -> ix -> Bool) ->
[Term ix cff] -> [Term ix cff] -> [Term ix cff]
addAlong ord (Term i x : tms1) (Term j y : tms2) = []