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



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

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

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

我想将(也许是细胞)定义为幺半群,如下所示:

instance Monoid (Maybe Cell) where
mempty             = Nothing
mappend Nothing x  = x
mappend (Just x) _ = (Just x)

和板作为另一个幺半群

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)

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

我试图做的是隐藏(Maybe a)GHC.BaseMonoid实例,以避免重复的实例。我尝试了很多搜索,但真的找不到隐藏它的方法。我无法隐藏所有Monoid或全部Semigroup,因为我需要它们的函数,但我需要隐藏这个特定的实例声明。谁能帮我?

注意:我使用的是灵活实例。

我标准的Haskell,类实例总是"完全全局的">- 如果一个类型在某处有一个给定类的实例,那么这个实例就会在任何地方使用。

因此,如果要定义一个单独的实例,则需要具有不同的类(通常不实用,包括在您的示例中)或不同的类型,这通常不是问题。事实上,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 = (<>)

同样,您应该使用具有合适Monoid实例的另一种类型,而不是Maybe Cell。这个实际上已经存在于基础库中,但实际上没有必要:你可以为Cell本身创建一个半群(不是幺半群!)实例,它代表左偏,然后Maybe将自动(因为GHC-8.4)具有所需的行为。

instance Semigroup Cell where
a <> _ = a

实际上,有人提议放宽这一点,允许本地选择实例,在2018年Haskell研讨会上发表的一篇论文中。

最新更新