我可以为可以包含其自身列表的类型实现Eq吗?

时间:2018-03-08 11:35:50

标签: idris

此代码无法编译:

data Foo = A String | B (List Foo)

Eq Foo where
  (==) (A x) (A y) = x == y
  (==) (B xs) (B ys) = xs == ys
  (==) _ _ = False

它会产生以下错误:

  

类型检查./eq.idr eq.idr:11:3-27:| 11 | (==)(A x)(A y)=   x == y | ~~~~~~~~~~~~~~~~~~~~~~~~~ Prelude.Interfaces.Main.Foo   Prelude.Interfaces.Eq的实现,方法==可能不是   总计由于递归路径Prelude.Interfaces.Main.Foo实现   of Prelude.Interfaces.Eq,method == - > Prelude.Interfaces.Main.Foo   Prelude.Interfaces.Eq,method ==

的实现

这里的问题是,我们在实现中依赖Eq Foo,因此递归路径?这似乎没有解释它,因为这编译:

data Bar = C String | D Bar

Eq Bar where
  (==) (C x) (C y) = x == y
  (==) (D x) (D y) = x == y
  (==) _ _ = False

所以 - 我可以对我在定义实现的任何内容进行==的递归调用,但我不能列出它的列表?我是否错过了使这项工作成功的技巧,或者我是否正试图做一些从根本上被破坏的事情?

2 个答案:

答案 0 :(得分:0)

决定相等性的函数需要在相互递归方面实现,就像Foo本身是由相互递归定义一样。

Eq Foo where
  (==) = eq
    where mutual -- massive indentation necessary
            eq : Foo -> Foo -> Bool
            eq (A l) (A r) = l == r
            eq (B l) (B r) = eq1 l r
            eq _ _ = False 
            eq1 : List Foo -> List Foo -> Bool
            eq1 (l :: ls) (r :: rs) = eq l r && eq1 ls rs
            eq1 [] [] = True
            eq1 _ _ = False

您不能重复使用List自己的(==),但可以将模式分解出来。

zipWithFoldrElem : (ls : List a) -> (rs : List a) -> ((l : a) -> (r : a) -> Elem (l, r) (zip ls rs) -> b) -> (b -> Lazy b -> b) -> b -> b -> b -> b
zipWithFoldrElem [] [] _ _ e _ _ = e
zipWithFoldrElem [] (_ :: _) _ _ _ el _ = el
zipWithFoldrElem (_ :: _) [] _ _ _ _ er = er
zipWithFoldrElem (l :: ls) (r :: rs) f g e el er = f l r Here `g` zipWithFoldLazyElem ls rs (\l, r, e => f l r (There e)) g e el er

Eq Foo where
  (A l) == (A r) = l == r
  (B l) == (B r) = zipWithFoldrElem l r (f l r) (&&) True False False
   where f : (ls : List Foo) -> (rs : List Foo) -> (l : Foo) -> (r : Foo) -> Elem (l, r) (zip ls rs) -> Bool
         f (l :: _) (r :: _) l r Here = l == r
         f (_ :: ls) (_ :: rs) l r (There e) = f ls rs l r e
         f [] [] _ _ _ impossible
  _ == _ = False

而且,对于show,这是一个词典Ord

Ord Foo where
  compare (A l) (A r) = l `compare` r
  compare (B l) (B r) = zipWithFoldrElem l r (f l r) thenCompare EQ LT GT
    where f : (ls : List Foo) -> (rs : List Foo) -> (l : Foo) -> (r : Foo) -> Elem (l, r) (zip ls rs) -> Ordering
          f (l :: _) (r :: _) l r Here = l `compare` r
          f (_ :: ls) (_ :: rs) l r (There e) = f ls rs l r e
          f [] [] _ _ _ impossible
  compare (A _) (B _) = LT
  compare (B _) (A _) = GT

答案 1 :(得分:0)

现在我已经有了一个更好的方式,我已经WellFounded了一下。

此数据类型为FooElemList。它会告诉您在Foo中找到子Foo的位置。

-- s is smaller than b if either ...
data Smaller : (s : Foo) -> (b : Foo) -> Type where
  -- it is one of the elements of ss when b = B ss
  Surface : Elem s ss -> Smaller s (B ss)
  -- it is smaller than one of the elements of ms when b = B ms
  -- "m" is for "medium"
  Deeper  : Smaller s m -> Elem m ms -> Smaller s (B ms)

SmallerWellFounded

WellFounded Smaller where
  wellFounded x = Access (acc x)
    where mutual
            acc : (b : Foo) -> (s : Foo) -> Smaller s b -> Accessible Smaller s
            acc (B ss) s (Surface e) = accSurface ss s e
            acc (B ms) s (Deeper {m} sr e) = accDeeper ms m e s sr
            accSurface : (ss : List Foo) -> (s : Foo) -> Elem s ss -> Accessible Smaller s
            accSurface (s :: _) s Here = Access (acc s)
            accSurface (_ :: ss) s (There e) = accSurface ss s e
            accDeeper : (ms : List Foo) -> (m : Foo) -> Elem m ms -> (s : Foo) -> Smaller s m -> Accessible Smaller s
            accDeeper (m :: _) m Here s sr = acc m s sr
            accDeeper (_ :: ms) m (There e) s sr = accDeeper ms m e s sr

这使用了相互递归,就像我的旧答案一样,但它更清晰地抽象出来。 WellFounded实例的一般框架是:

WellFounded (rel : a -> a -> Type) where -- rel s b means s is smaller than b
  wellFounded x = Access (acc x)
    where acc : (b : a) -> (s : a) -> rel s b -> Accessible rel s
    -- Accessible is really cryptic:
    -- Access : (rec : (y : a) -> rel y x -> Accessible rel y) -> Accessible rel x
    -- but the idea of acc is simple:
    -- we convince the totality checker s is really smaller than b
    -- acc needs to be recursive, and when it convinces the totality
    -- checker, it can end with Access (acc s)

WellFounded买了我们

wfRec : WellFounded rel =>
        (step : (x : a) ->
                (rec : (y : a) -> rel y x -> b) ->
                b
        ) ->
        a -> b

step是由开放递归定义的函数。 step得到一个参数recstep调用rec而不是递归到自身,并证明其正在递归的参数是正确的大小,wfRec将回拨路由回step(基本上是fix : (step : (rec : (a -> b)) -> (x : a) -> b) -> a -> b; fix f x = f (fix f) x但总共。{/ p>

我们现在可以干净利落地排除列表上(==)的逻辑:

eqBy : (ls : List a) -> (rs : List a) -> ((l : a) -> (r : a) -> (el : Elem l ls) -> (er : Elem r rs) -> Bool) -> Bool
eqBy (l :: ls) (r :: rs) eq = eq l r Here Here && eqBy ls rs (\l, r, el, er => eq l r (There el) (There er))
eqBy [] [] _ = True
eqBy _ _ _ = False

Eq实例并不比天真的实例差多少:

Eq Foo where
  (==) = wfRec step
    where step : (x : Foo) -> ((y : Foo) -> Smaller y x -> Foo -> Bool) -> Foo -> Bool
          step (A l) _ (A r) = l == r
          step (B l) rec (B r) = eqBy l r (\l, r, el, er => rec l (Surface el) r)
          step _ _ _ = False

eqByWellFounded实例现在比我之前创建的zipWithFoldrElem憎恶更可重复使用。