Purescript 守卫失败并显示:未找到 Control.MonadZero.MonadZero 身份的类型 class 实例

Purescript guard fails with: No type class instance found for Control.MonadZero.MonadZero Identity

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

我有一些简单的代码来访问由自定义数据类型表示的节点。我认为像 guard 这样的 MonadZero 控制函数应该在所有 monad 结构中工作(例如本例中的 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

None 其中帮助或编译,我很确定我误解了这里的问题。我如何在这种情况下使用 guard 或任何其他 monadic 检查?

编辑:这个答案解决了问题中直接指出的问题,例如: guard 用法,MonadPlus 上下文和 newtype 推导。

我认为@Fyodor Soikin 的答案通过将 guard 替换为 when 解决了这个问题的本质,所以这个答案可以被视为补充 material.

我认为如果你尝试这样的事情:

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

它应该可以工作,因为 MaybeMonadZero 个实例,而 StateT 个实例依赖于此。

现在让我们回头尝试解决您遇到的一些问题。

It gives me the error:

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

此消息告诉我们 Identity 没有 MonadZero 实例。如果我们检查什么是 MonadZero we are going to discover that it is a class which implicates that given type has also Monad and Alternative instance and which satisfies the Annihilation law... Identity has no Alternative instance because it require that given type has a 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

我认为当我们只有一个构造函数 Identity ∷ ∀ a. a → Identity a.

时,不可能为 empty(其中 empty :: ∀ a. f a)值找到任何好的候选者

例如,在 Maybe 的情况下,我们有 empty = Nothing<|>,此值总是给出 Nothing

Which I don't understand why MonadZero would not work in this context, but regardless, I attempted to derive the Identity for MonadZero with things like this:

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

当您使用新类型派生时,您是在告诉编译器您的 newtype 实例应该使用 "inner type" 实例作为实现。在这种情况下,您只有 a 类型参数,手头没有 "underlyning" 实例。

我认为如果你想使用这样的派生,你必须使用你想要使用的实例的具体类型。例如,这里我们为我们的类型 MaybeWrapper 派生 Functor,它使用 Maybe 实例来提供适当的成员实现(在这种情况下为 map):

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

快乐的 PureScript 黑客!

这里你需要的是when,而不是guard

guard 仅适用于有可能 not 产生结果的单子。这种 monad 的一个例子是 Maybe,其中 guard 将在条件为假时产生 Nothing。另一个示例是 Array,其中 guard 会在条件为假时生成一个空数组。等等。

在你的例子中,你的 monad 总是 产生一个值,所以 guard 在那里真的无关紧要。

相反,如果我正确理解你的逻辑,你要做的是在条件为真时产生效果,而在条件为假时跳过产生效果。这可以通过 when or its evil twin unless:

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

另请注意,您没有在 modify 下使用参数 acc。我用下划线替换了它,但实际上,如果你不使用参数,你不需要 modify,你需要 put:

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

但接下来要注意的是 get 然后立即 put 的模式正是 modify 的用途。因此,在您的情况下,看到 getput 之间没有任何影响,我实际上会将所有逻辑放在 modify 本身中:

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

效果越差越好