Сбой защиты Purescript: не найден экземпляр класса типа для идентификатора Control.MonadZero.MonadZero - PullRequest
0 голосов
/ 03 ноября 2018
 visitNode :: Castle -> State (Set Castle) Unit
 visitNode c = do
     s <- get
     guard $ not (member c s)
     modify \acc -> insert c s

У меня есть простой код для посещения узлов, представленный пользовательским типом данных. Я думал, что функции управления MonadZero, такие как защита, должны работать во всех монадных структурах (например, в данном случае State). Это дает мне ошибку:

No type class instance was found for Control.MonadZero.MonadZero Identity

Что я не понимаю, почему MonadZero не будет работать в этом контексте, но независимо от этого, я попытался получить удостоверение для MonadZero с помощью таких вещей:

 newtype Identity a = Identity a
 derive instance newtypeIdentity :: Newtype (Identity a) _
 derive newtype instance monadZeroIdentity :: MonadZero Identity

Ничего из этого не помогло или не скомпилировано, и я вполне уверен, что неправильно понял, что здесь не так. Как мне использовать охрану или любые другие монадические проверки в этом контексте?

Ответы [ 2 ]

0 голосов
/ 06 ноября 2018

Здесь вам нужно when, а не guard.

guard работает только для монад, где есть возможность , а не , чтобы получить результат. Одним из примеров такой монады будет Maybe, где guard даст Nothing, когда условие ложно. Другим примером будет Array, где guard даст пустой массив, когда условие ложно. И так далее.

В вашем случае ваша монада всегда выдает значение, поэтому guard действительно не имеет значения.

Вместо этого, если я правильно понял вашу логику, то, что вы хотите сделать, это произвести эффект, когда условие истинно, и пропустить его создание, когда условие ложно. Это может быть достигнуто через when или его злого близнеца unless:

visitNode c = do
     s <- get
     unless (member c s) $ 
         modify \_-> insert c s

Также обратите внимание, что вы не используете параметр acc в modify. Я заменил его подчеркиванием, но на самом деле, если вы не используете аргумент, вам не нужно modify, вам нужно put:

visitNode c = do
     s <- get
     unless (member c s) $ 
         put (insert c s)

Но следующее, что следует заметить, это то, что шаблон get, а затем сразу put - это именно то, для чего modify предназначен. Итак, в вашем случае, видя, что между get и put нет эффектов, я бы фактически поместил всю логику в modify:

visitNode c = modify \s ->
    if member c s 
        then insert c s 
        else s

Чем меньше эффект, тем лучше.

0 голосов
/ 05 ноября 2018

РЕДАКТИРОВАТЬ: Этот ответ решает вопросы, прямо указанные в вопросе, как: guard использование, MonadPlus контекст и newtype получение.

Я думаю, что ответ @Fyodor Soikin решает суть этой проблемы, заменяя guard на when, поэтому этот ответ можно рассматривать как дополнительный материал.

Я думаю, что если вы попробуете что-то вроде:

visitNode :: Castle -> StateT (Set Castle) Maybe Unit
visitNode c = do
  s <- get
  guard $ not (member c s)
  modify \acc -> insert c s

это должно работать, потому что Maybe имеет экземпляр MonadZero, а экземпляр StateT зависит от этого.

Теперь давайте вернемся и попробуем решить некоторые проблемы, с которыми вы столкнулись.

выдает ошибку:

No type class instance was found for 
   Control.MonadZero.MonadZero Identity

Это сообщение говорит нам, что у Identity нет экземпляра MonadZero. Если мы проверим, что такое MonadZero, мы обнаружим, что это класс, который подразумевает, что данный тип имеет также экземпляр Monad и Alternative и который удовлетворяет закону Annihilation. . Identity не имеет экземпляра Alternative, так как требует, чтобы данный тип имел Plus экземпляр:

The Plus type class extends the Alt type class with a value that should be the left and right identity for (<|>)

(...)

Members:

empty :: forall a. f a

Я думаю, что невозможно найти какого-либо хорошего кандидата для значения empty (где empty :: ∀ a. f a), когда у нас есть только один конструктор Identity ∷ ∀ a. a → Identity a.

Например, в случае Maybe у нас есть empty = Nothing и <|> с этим значением всегда дает Nothing.

Что я не понимаю, почему MonadZero не будет работать в этом контексте, но независимо от этого, я попытался получить Identity для MonadZero с помощью таких вещей:

newtype Identity a = Identity a
derive instance newtypeIdentity :: Newtype (Identity a) _
derive newtype instance monadZeroIdentity :: MonadZero Identity

Когда вы используете наследование newtype, вы говорите компилятору, что экземпляр для вашего newtype должен использовать экземпляр "внутреннего типа" в качестве реализации. В этом случае у вас есть только параметр типа a, и «лежащего в основе» экземпляра нет.

Я думаю, что если вы хотите использовать такое производное, вы должны использовать конкретный тип, какие экземпляры вы хотите использовать. Например, здесь мы извлекаем Functor для нашего типа MaybeWrapper, который использует экземпляр Maybe для обеспечения соответствующей реализации членов (в данном случае map):

newtype MaybeWrapper a = MaybeWrapper (Maybe a)
derive instance newtypeMaybeWrapper :: Newtype (MaybeWrapper a) _
derive newtype instance functorMaybeWrapper :: Functor MaybeWrapper

Счастливого взлома PureScript!

...