意外的重叠实例错误

时间:2017-02-05 15:40:37

标签: haskell types monads overlapping-instances

我有以下代码。 Class1个实例说明了直接超类是什么类,SuperClass1会自动遍历Class1以查找所有超类。 (我省略了这些类的实际方法,因为它们与我的问题无关。)

{-# LANGUAGE PolyKinds, RankNTypes, ConstraintKinds, FlexibleInstances, UndecidableInstances, MultiParamTypeClasses, FunctionalDependencies #-}

class Class1 b h | h -> b
instance Class1 Functor Applicative
instance Class1 Applicative Monad

class SuperClass1 b h
instance {-# OVERLAPPING #-} SuperClass1 b b
instance {-# OVERLAPPABLE #-} (SuperClass1 b c, Class1 c h) => SuperClass1 b h

这很棒!现在我想这样使用它:

newtype HFree c f a = HFree { runHFree :: forall g. c g => (forall b. f b -> g b) -> g a }

instance SuperClass1 Functor c => Functor (HFree c f)
instance SuperClass1 Applicative c => Applicative (HFree c f)
instance SuperClass1 Monad c => Monad (HFree c f)

test :: (a -> b) -> HFree Monad f a -> HFree Monad f b
test = fmap

(即每当FunctorHfree c f的超类时,我可以为Functor提供c的实例。)

这在Applicative实例中给出了这个错误(和Monad实例类似):

• Overlapping instances for SuperClass1 Functor c1
    arising from the superclasses of an instance declaration
  Matching instances:
    instance [overlappable] forall k k k (b :: k) (c :: k) (h :: k).
                            (SuperClass1 b c, Class1 c h) =>
                            SuperClass1 b h
      -- Defined at superclass.hs:17:31
    instance [overlapping] forall k (b :: k). SuperClass1 b b
      -- Defined at superclass.hs:16:30
  (The choice depends on the instantiation of ‘c1, k1’
   To pick the first instance above, use IncoherentInstances
   when compiling the other instance declarations)
• In the instance declaration for ‘Applicative (HFree c f)’

据我了解,发生的情况是Applicative实例需要Functor实例,因此Applicative实例还需要Functor的SuperClass1 Functor c约束。事实上,如果我添加它,错误就会消失。 (这就是我目前所拥有的:http://hackage.haskell.org/package/free-functors-0.7/docs/Data-Functor-HFree.html

但不知何故,GHC足够明智地理解SuperClass1 Applicative c暗示SuperClass1 Functor c,因为它并没有抱怨缺少约束。相反,它会陷入重叠的实例错误。如果有办法解决错误会很好,但我无法弄清楚如何!

1 个答案:

答案 0 :(得分:3)

  

但不知何故,GHC足够明智地理解SuperClass1 Applicative c暗示SuperClass1 Functor c,因为它并没有抱怨缺少约束。

我担心你太有希望了 - GHC可能会从另一端开始行动并立即放弃:它看到fmap并试图检查HFreeFunctor 。它只有一个实例可供选择:SuperClass1 Functor c => Functor (HFree c f)。然后,它开始尝试满足该实例的约束(SuperClass1 Functor c)并突然意识到它不知道该怎么做 - 它可以选择两个实例:

  • SuperClass1 b b
  • SuperClass1 b h

请注意,我省略了对这些实例的约束 - 这是因为 GHC需要在查看左侧的约束之前提交实例。话虽如此,@ user2407038的评论是完全正确的:您的实例重叠非常严重 - GHC不知道它是否应该尝试统一b ~ Functorb ~ cb ~ Functor和{{ 1}}。两者都可以发挥作用。

如果是选择 的情况,则应启用h ~ c。不幸的是,这不是这种情况。您知道您希望选择第二个实例,但GHC不会。

我最近一直toying around with a similar sort of problem but in relation to subtyping但是无论你如何处理它,实例解析真的很难。我给你的建议是尽可能使用类型系列。

修改

这是一个让代码编译的解决方案,除了依赖类型系列而不是类型类。设置机器是

IncoherentInstances

然后,应用于{-# LANGUAGE PolyKinds, ConstraintKinds, TypeFamilies, UndecidableInstances, DataKinds, TypeOperators #-} import Data.Kind (Constraint) -- Find transitively all the superclasses of a constraint (including itself) type family SuperClasses (x :: k -> Constraint) :: [k -> Constraint] type instance SuperClasses Functor = '[Functor] type instance SuperClasses Applicative = Applicative ': SuperClasses Functor type instance SuperClasses Monad = Monad ': SuperClasses Applicative -- Type level version of `elem` which is a Constraint type family Elem (x :: k) (xs :: [k]) :: Constraint where Elem a (a ': bs) = () Elem a (b ': bs) = Elem a bs -- Type level version of checking the first list is a subset of the second type family Subset (xs :: [k]) (ys :: [k]) :: Constraint where Subset '[] bs = () Subset (a ': as) bs = (Elem a bs, Subset as bs) -- Tell us whether the constraint x is a sub-constraint (thereby implied by) y type x <: y = SuperClasses x `Subset` SuperClasses y FunctorApplicative,我们需要

Monad

以下是一些测试

-- I've cropped the body of HFree since it is of no interest here
data HFree c f a

instance Functor     <: c => Functor     (HFree c f)
instance Applicative <: c => Applicative (HFree c f)
instance Monad       <: c  => Monad      (HFree c f)