1

我正在阅读HList图书馆的代码。有一个HBetween类是一个类型级别的函数,它接受一个HNatn 并返回一个HNat形成范围 [HZero, n) 的 s 列表。我想实现另一个类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 instance HRange l u r => HRange l (HSucc u) (HCons u r)。任何解释表示赞赏!

4

1 回答 1

3

Note that GHC gives the type of hRange hZero (hSucc hZero) as HRange HZero (HSucc HZero) (HCons Zero HNil). Because the GHC instance matcher does not consider constraints before selecting matches, it just checks against HRange l (HSucc u) (HCons u r). l can match anything, so matches HZero; HSucc u clearly matches HSucc HZero, as do HCons u r and HCons HZero HNil.

In general, as far as GHC is concerned, your second instance is a strict superset of the first, since the only difference in the right sides (thus ignoring the constraints HNat l and HRange l u r), when selecting an instance the first is the second with r restricted to be HNil. The normal approach to this problem is to throw in a phantom type or class to differentiate the right sides, although off the top of my head I do not see how to do that here.

于 2013-05-11T08:05:51.747 回答