简体   繁体   中英

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

I have some simple code for visiting nodes represented by a custom datatype. I thought MonadZero control functions like guard are supposed to work within all monad structures (such as State in this case). It gives me the error:

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

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

None of which helped or compiled and I'm fairly sure I misunderstand what is wrong here. How do I use guard or any other monadic checks in this context?

What you need here is when , not guard .

guard only works for monads where there is a possibility to not produce a result. One example of such monad would be Maybe , where guard will yield Nothing when the condition is false. Another example would be Array , where guard would yield an empty array when the condition is false. And so on.

In your case, your monad always produces a value, so guard is really irrelevant there.

Instead, if I understood your logic correctly, what you want to do is produce an effect when a condition is true, and skip producing it when the condition is false. This can be accomplished via when or its evil twin unless :

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

Also note that you're not using the parameter acc under modify . I've replaced it with an underscore, but really, if you're not using the argument, you don't need modify , you need put :

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

But the next thing to notice is that the pattern of get and then immediately put is exactly what modify is for. So in your case, seeing how there are no effects in between get and put , I would actually put all the logic within modify itself:

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

The less effectful, the better.

EDIT: This answer tackles issues directly pointed in the question like: guard usage, MonadPlus context and newtype deriving.

I think that @Fyodor Soikin answer tackles the essence of this problem by replacing guard with when so this answer can be treated as supplementary material.

I think that if you try something like:

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

it should work because Maybe has MonadZero instance and StateT instance depends on this.

Now let's go back and try to resolve some of the problems which you have encountered.

It gives me the error:

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

This message tells us that Identity has no MonadZero instance. If we check what is a 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 instance:

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

I think that it is impossible to find any good candidate for an empty (where empty :: ∀ a. fa ) value when we have only one constructor Identity ∷ ∀ a. a → Identity a Identity ∷ ∀ a. a → Identity a .

For example in case of Maybe we have empty = Nothing and <|> with this value always gives 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 

When you are using newtype deriving you are telling the compiler that instance for your newtype should use "inner type" instance as an implementation. In this case you have only a type parameter and there is no "underlyning" instance at hand.

I think that if you want to use such a deriving you have to use concrete type which instances you want to use. For example here we are deriving Functor for our type MaybeWrapper which uses Maybe instance to provide appropriate members implementation ( map in this case):

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

Happy PureScript Hacking!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM