【问题标题】:Overlapping instances that don't seem to overlap似乎不重叠的重叠实例
【发布时间】:2019-01-09 00:01:51
【问题描述】:

考虑以下(接近最小的)示例:

{-# Language FlexibleInstances #-}

class Predicate a where
    test :: a -> Bool

instance (Predicate a, Traversable t) => Predicate (t a) where
    test = all test

data Bar = Bar
instance Predicate Bar where
    test Bar = False

data Baz a = Baz a
instance Predicate a => Predicate (Baz a) where
    test (Baz x) = test x

main :: IO ()
main = print $ test $ Baz Bar

查看test $ Baz Bar,您会期待False 的结果,因为我们有实例Predicate BarPredicate a => Predicate (Baz a)

但是 GHC 8.6.3 和 8.0.1 都拒绝这个:

test.hs:18:16: error:
    • Overlapping instances for Predicate (Baz Bar)
        arising from a use of ‘test’
      Matching instances:
        instance (Predicate a, Traversable t) => Predicate (t a)
          -- Defined at test.hs:6:10
        instance Predicate a => Predicate (Baz a)
          -- Defined at test.hs:14:10
    • In the second argument of ‘($)’, namely ‘test $ Baz Bar’
      In the expression: print $ test $ Baz Bar
      In an equation for ‘main’: main = print $ test $ Baz Bar
   |
18 | main = print $ test $ Baz Bar
   |                ^^^^^^^^^^^^^^

但没有重叠:我们可以通过注释掉 Predicate (Baz a) 实例来确认没有 Traversable Baz 实例,在这种情况下我们会收到错误:

test.hs:18:16: error:
    • No instance for (Traversable Baz) arising from a use of ‘test’
    • In the second argument of ‘($)’, namely ‘test $ Baz Bar’
      In the expression: print $ test $ Baz Bar
      In an equation for ‘main’: main = print $ test $ Baz Bar
   |
18 | main = print $ test $ Baz Bar
   |                ^^^^^^^^^^^^^^

我假设这是FlexibleInstances 的限制?如果是,为什么?是否有批准的解决方法?


好的,事实证明这是 GHC 决定独立使用实例约束的结果,如 here 所述。不过,这个技巧在这里似乎不起作用:

instance (b ~ Baz, Predicate a) => Predicate (b a) where

给出Duplicate instance declarations 错误,因此我将问题留待解决,以便在这种情况下有效。

【问题讨论】:

    标签: haskell derivingvia


    【解决方案1】:

    问题在于这些实例确实重叠,因为实例解析机制在决定采用哪个实例时只查看实例头部,并且仅在稍后,选择了一个实例之后,它才会检查看到它被满足的约束(否则抛出和错误)。

    我建议阅读instance resolution 上的文档

    解决问题的一种方法(除了重新设计解决方案,这可能是正确的做法)是告诉 GHC 某个实例“不太重要”(或可重叠)。
    这基本上意味着 GHC 将选择一个更具体的实例(如果它可用)(更具体的意思是您可以在上面链接的文档中阅读)。
    这是通过使用 pragma {-# OVERLAPPABLE #-}{-# OVERLAPS #-} 来实现的(阅读文档以了解区别,基本上前者更具体)。

    生成的代码看起来像这样

    {-# Language FlexibleInstances #-}
    
    class Predicate a where
        test :: a -> Bool
    
    instance {-# OVERLAPPABLE #-} (Predicate a, Traversable t) => Predicate (t a) where
        test = all test
    
    data Bar = Bar
    instance Predicate Bar where
        test Bar = False
    
    data Baz a = Baz a
    instance Predicate a => Predicate (Baz a) where
        test (Baz x) = test x
    
    main :: IO ()
    main = do
       print . test $ Baz Bar
       print . test $ ([] :: [Bar])
       print . test $ [Bar]
       print . test $ Baz ([] :: [Bar])
    

    运行结果是

    False
    True
    False
    True
    

    正如预期的那样。

    【讨论】:

      【解决方案2】:

      使用DerivingVia,您可以将此行为赋予新类型

      type    WrappedPredicate :: (k -> Type) -> k -> Type
      newtype WrappedPredicate t a = WrapPredicate (t a)
        deriving
        stock Foldable
      
      -- Foldable is sufficient
      instance (Foldable t, Predicate a) => Predicate (WrappedPredicate t a) where
        test :: WrappedPredicate t a -> Bool
        test = all test
      

      并通过它派生Predicate,这比您给出的重叠实例更可取。

      {-# Language DerivingVia #-}
      
      type Tree :: Type -> Type
      data Tree a = Leaf a | Branch (Tree a) (Tree a)
        deriving
        stock Foldable
      
        deriving Predicate
        via WrappedPredicate Tree a
      

      就像Ap f a newtype 是比定义与Monoid [a] 和其他常见实例不兼容的实例更好的解决方案。

      instance (Applicative f, Semigroup a) => Semigroup (f a) where
        (<>) :: f a -> f a -> f a
        (<>) = liftA2 (<>)
      
      instance (Applicative f, Monoid a) => Monoid (f a) where
        mempty :: f a
        mempty = pure mempty
      

      【讨论】:

        猜你喜欢
        • 2019-09-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-01-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多