Purescript Guard失败,原因:没有找到Control.MonadZero.MonadZero Identity的类型类实例

时间:2018-11-03 00:15:20

标签: monads purescript

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

我有一些简单的代码可以访问由自定义数据类型表示的节点。我认为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

它们都没有帮助或编译,我敢肯定我会误解这里的问题。在这种情况下,我该如何使用警卫检查或其他任何单子检查?

2 个答案:

答案 0 :(得分:1)

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

guard仅适用于可能产生结果的单子。这种单子的一个示例是Maybe,其中,当条件为false时,guard将产生Nothing。另一个示例为Array,其中,条件为false时,guard将产生一个空数组。依此类推。

在您的情况下,您的monad总是 产生一个值,所以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的目的。因此,在您的情况下,看看getput之间没有任何影响,我实际上将所有逻辑放在modify本身内:

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

效果越差越好。

答案 1 :(得分:0)

编辑:此答案解决了直接在问题中指出的问题,例如: 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

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

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

  

它给了我错误:

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

此消息告诉我们Identity没有MonadZero实例。如果检查什么是MonadZero,我们将发现它是一个类,它暗示给定类型也具有MonadAlternative实例,并且满足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在这种情况下不起作用,但是无论如何,我试图通过以下方式得出MonadZero的身份:

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

使用新类型派生时,您是在告诉编译器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黑客攻击!