Скрытие объявлений экземпляров класса типов при импорте в Haskell - PullRequest
0 голосов
/ 23 февраля 2019

Я пытаюсь сделать игру в крестики-нолики, и я решил построить типы для ячеек (элементов доски) и доски следующим образом:

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

Здесь Ничто не представляетпустая ячейка, (Just X) и (Just O) представляют ячейки, заполненные соответственно X и O.

Я хотел бы определить (может быть Cell) как моноид следующим образом:

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

И Board как еще один моноид с

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 экземпляр (Maybe a) от GHC.Base, чтобы избежать дублирования экземпляров.Я много пытался найти это, но не смог найти способ это скрыть.Я не могу скрыть все Monoid или все Semigroup, потому что мне нужны их функции, но мне нужно скрыть это объявление конкретного экземпляра.Может ли кто-нибудь помочь мне с этим?

ПРИМЕЧАНИЕ: я использую FlexibleInstances.

1 Ответ

0 голосов
/ 23 февраля 2019

В стандартном 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 = (<>)

Аналогично, вместо Maybe Cell вы должны использовать другой тип, который имеет подходящий экземпляр Monoid.Этот на самом деле уже существует в базовой библиотеке , но в этом нет особой необходимости: вы можете просто создать экземпляр полугруппы (не моноид!) Для самого Cell, который представляет левое смещение, затем Maybeбудет (начиная с GHC-8.4) автоматически иметь желаемое поведение.

instance Semigroup Cell where
  a <> _ = a

На самом деле было предложено ослабить это, разрешив локально выбранные экземпляры,в документ, представленный на симпозиуме Haskell 2018 года .

...