我正在阅读HList
库的代码。有一个HBetween
类,它是一个类型级函数,它带有HNat
n并返回一个HNat
s列表,形成一个范围[HZero,n)。我想实现另一个类HRange
,它有一个重载函数hRange :: l -> u -> r
,它接受一个低限l
和一个上限u
并返回一个范围[l,u )。我的代码如下(为了使代码更清晰,hRange
的结果是相反的顺序,比如说(u,l))
{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies,
FlexibleInstances, FlexibleContexts, UndecidableInstances #-}
module Data.HList.HNats where
import Data.HList.CommonMain
class (HNat l, HNat u) => HRange l u r | l u -> r where
hRange :: l -> u -> r
instance HNat l => HRange l (HSucc l) (HCons l HNil) where
hRange _ _ = undefined
instance HRange l u r => HRange l (HSucc u) (HCons u r) where
hRange _ _ = undefined
我在ghci中尝试了这个代码,发生了意想不到的事情:
*Data.HList.HNats Data.HList> :load Data/HList/HNats
[1 of 1] Compiling Data.HList.HNats ( Data/HList/HNats.hs, interpreted )
Ok, modules loaded: Data.HList.HNats.
*Data.HList.HNats Data.HList> hRange hZero (hSucc hZero )
<interactive>:24:1:
Overlapping instances for HRange
HZero (HSucc HZero) (HCons HZero HNil)
arising from a use of `hRange'
Matching instances:
instance HNat l => HRange l (HSucc l) (HCons l HNil)
-- Defined at Data/HList/HNats.hs:14:10
instance HRange l u r => HRange l (HSucc u) (HCons u r)
-- Defined at Data/HList/HNats.hs:20:10
In the expression: hRange hZero (hSucc hZero)
In an equation for `it': it = hRange hZero (hSucc hZero)
*Data.HList.HNats Data.HList>
我不知道为什么hRange hZero (hSucc hZero )
可以匹配实例instance HRange l u r => HRange l (HSucc u) (HCons u r)
。任何解释都表示赞赏!
答案 0 :(得分:3)
请注意,GHC将hRange hZero (hSucc hZero)
的类型设为HRange HZero (HSucc HZero) (HCons Zero HNil)
。因为GHC实例匹配器在选择匹配之前不考虑约束,所以它只检查HRange l (HSucc u) (HCons u r)
。 l
可以匹配任何内容,因此匹配HZero
; HSucc u
与HSucc HZero
和HCons u r
明显匹配HCons HZero HNil
。
一般来说,就GHC而言,你的第二个实例是第一个实例的严格超集,因为右边的唯一差异(因此忽略约束HNat l
和HRange l u r
),在选择实例时,第一个是r
限制为HNil
的第二个实例。解决这个问题的正常方法是投入一个幻像类型或类来区分右侧,虽然我不知道如何在这里做到这一点。