Reputation: 3
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?
Upvotes: 0
Views: 264
Reputation: 2231
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. f a
) value when we have only one constructor 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!
Upvotes: 0
Reputation: 80805
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.
Upvotes: 1