我可以告诉 GHC 随意选择使用哪个实例,因为我不在乎吗?

Posted

技术标签:

【中文标题】我可以告诉 GHC 随意选择使用哪个实例,因为我不在乎吗?【英文标题】:Can I tell GHC to arbitrarily select which instance to use, because I don't care? 【发布时间】:2016-03-31 06:09:08 【问题描述】:

我有一些这样的代码:

-# OPTIONS_GHC -Wall #-
-# LANUAGE VariousLanguageExtensionsNoneOfWhichWorked #-

import Control.Applicative
import Data.Either
import Data.Void

class Constructive a where
    lem :: Either (a -> Void) a

instance Constructive Void where
    lem = Left id

instance Num a => Constructive a where
    lem = Right 0

instance Enum a => Constructive a where
    lem = Right $ toEnum 0

instance Bounded a => Constructive a where
    lem = Right minBound

instance Monoid a => Constructive a where
    lem = Right mempty

instance Alternative f => Constructive (f a) where
    lem = Right empty

问题是,GHC 抱怨

pad.hs:49:10:
    Duplicate instance declarations:
      instance [overlap ok] Bounded a => Constructive a
        -- Defined at pad.hs:49:10
      instance [overlap ok] Monoid a => Constructive a
        -- Defined at pad.hs:52:10

还有一堆类似的错误。

有没有办法告诉 GHC 随机选择一个,因为我不在乎它使用哪个? (我什至不在乎每次我使用lem 时它是否会选择一个不同的,因为这无关紧要。)

【问题讨论】:

您是否尝试将实例拆分为更多(非重叠)模块? GHC 不能简单地选择任意实例,因为它们的上下文可能不满足。要选择一个,GHC 需要进行回溯,但它的设计目的是不这样做。 所有这些重叠的实例都实现了一个目的——为某种类型构造一些默认值。例如,您不使用 Monoid 的 mappend 或 Enum 的 succ,因此在这里使用这些约束肯定是错误的(语义上)。幸运的是,有一个library 捕获了名为Default 的类型类中的“默认值”关系,允许您编写一个实例:instance Default a => Constructive a where lem = Right default 【参考方案1】:

这并不是您问题的真正答案,更像是一个扩展评论,建议如何解决问题的另一种途径。

在 Haskell 中,规范的解决方案是为您的每个实例创建一个 newtype,这可能不是您想要的。不过,我想建议您另一种方法。

在 Haskell 中,我们基本上有 3 种可能性来构造数据类型:

    使用产品和coproducts(不相交联合)的代数数据类型。 函数类型。 原始类型。

对于第一部分,我们可以使用SYB 或GHC Generics。如果一个产品是空的,或者有一个空因子,它会映射到a -> Void。如果它的所有加法都可以,则副产品映射到a -> Void

如果ab 都是,则函数类型a -> b 是构造函数:

instance (Constructive a, Constructive b) => Constructive (a -> b) where
  ...

如果x :: b 不为空,则a -> bconst x 占据。如果a 为空,则a -> babsurd 居住。如果a 非空且b 为空,则a -> b 映射到Void

所有 Haskell 原始类型都是非空的,因此它们的构造性很简单。

不幸的是,似乎没有办法告诉 GHC 所有数据类型都是这三种中的一种。我的建议是实现-> 的实例,然后

尝试使用 SYB 为所有实现 Data 的东西实现一个实例。仍然存在如何处理重叠实例的问题。或者: 尝试使用 GHC 泛型为 ADT 提供默认实例,并为原始类型手动实现实例。这意味着对于每种数据类型,您仍然必须提供一个空的 instance 实现,默认值由泛型提供。

写完之后,我发现了AdvancedOverlap。也许将它与以前的方法之一结合起来可能会产生一个很好的解决方案。

【讨论】:

以上是关于我可以告诉 GHC 随意选择使用哪个实例,因为我不在乎吗?的主要内容,如果未能解决你的问题,请参考以下文章

关于 GHC 实施的好的介绍性文字?

哪个信号可以告诉我未选中的复选框?

公司邮箱哪个好

GHC 7.8 中的角色代码损坏

限制 GHC 的内存使用

使用 -fPIC 支持编译 ghc