在Haskell中导入时隐藏类型类实例声明

时间:2019-02-22 21:22:06

标签: haskell typeclass monoids semigroup

我正在尝试制作井字游戏,因此我决定构造单元格(棋盘的元素)和棋盘的类型,如下所示:

Factor    P1     P2     P3     P4     P5
  A     TRUE   TRUE   TRUE  FALSE   TRUE
  B     TRUE   TRUE   TRUE   TRUE  FALSE
  C     TRUE   TRUE  FALSE   TRUE  FALSE

这里,Nothing代表一个空单元格,(Just X)和(Just O)分别代表填充有X和O的单元格。

我想按如下方式将(也许是单元格)定义为类半体:

data Cell  = X | O deriving (Show, Eq)
type Board = [[Maybe Cell]]

然后通过

登上另一个Monoid
instance Monoid (Maybe Cell) where
  mempty             = Nothing
  mappend Nothing x  = x
  mappend (Just x) _ = (Just x)

我知道我完全可以在没有monoid的情况下实现此功能,但是我正在尝试探索这一领域,这是一种非常整洁的编写方式。

我得到的问题是,instance Monoid Board where mempty = [[Nothing, Nothing, Nothing] ,[Nothing, Nothing, Nothing] ,[Nothing, Nothing, Nothing]] mappend = zipWith (zipWith mappend) -- where the right-hand-side mappend is the addition on (Maybe Cell) 中已经定义了一个Maybe单面体实例,如下所示:

GHC.Base

这个定义与我想要的定义非常不同,但是它导致重复的实例声明,所以我不能只是忽略它。

我想做的是从instance Semigroup a => Monoid (Maybe a) 隐藏Monoid的{​​{1}}实例,以避免重复的实例。我尝试了很多搜索,但是找不到真正的隐藏方法。我无法隐藏所有(Maybe a)或全部GHC.Base,因为我需要它们的功能,但是我需要隐藏此特定的实例声明。有人可以帮我吗?

注意:我正在使用FlexibleInstances。

1 个答案:

答案 0 :(得分:6)

在标准的Haskell中,类实例始终是“完全全局的” –如果类型具有给定类 somewhere 的实例,则该实例将在所有地方使用。

因此,如果您想定义一个单独的实例,则需要具有一个不同的类(通常在您的示例中并不实际),或者具有一个不同的类型(通常不成问题)。实际上,Haskell具有专门针对此类事物的关键字newtype。您只需将type Board = [[Maybe Cell]]更改为

newtype Board = Board [[Maybe Cell]]

然后

instance Semigroup Board where
  Board l <> Board r = Board $ zipWith (zipWith mappend) l r
instance Monoid Board where
  mempty = Board [[Nothing, Nothing, Nothing]
                 ,[Nothing, Nothing, Nothing]
                 ,[Nothing, Nothing, Nothing]]
  mappend = (<>)

同样,应该使用具有合适的Maybe Cell实例的另一种类型来代替Monoid。那个实际上是exists already in the base library,但这并不是必须的:您可以为Cell本身创建一个半群(不是monoid!)实例,它代表左偏,那么Maybe将(因为GHC-8.4)自动具有所需的行为。

instance Semigroup Cell where
  a <> _ = a

实际上已经建议放宽此设置,允许在a paper presented at the 2018 Haskell Symposium中使用本地选择的实例。