I am trying to get good at Monads and have written the following Monads and functions in which I use the >>
(in the apply
-function) although it is not declared in the Monad itself. How come this is possible to compile, as I understand http://learnyouahaskell.com/a-fistful-of-monads#walk-the-line it is required to declare it in the instantiation of the Monad as is the case with the Maybe Monad
.
data Value =
NoneVal
| TrueVal | FalseVal
| IntVal Int
| StringVal String
| ListVal [Value]
deriving (Eq, Show, Read)
data RunErr = EBadV VName | EBadF FName | EBadA String
deriving (Eq, Show)
newtype CMonad a = CMonad {runCMonad :: Env -> (Either RunErr a, [String]) }
instance Monad CMonad where
return a = CMonad (\_ -> (Right a, []))
m >>= f = CMonad (\env ->
(Left a, strLst) -> (Left a, strLst)
(Right a, strLst) -> let (a', strLst') = runCMonad (f a) env in (a', strLst' strLst))
output :: String -> CMonad ()
output s = CMonad(\env -> (Right (), [] [s]))
apply :: FName -> [Value] -> CMonad Value
apply "print" [] = output "" >> return NoneVal
Furthermore, how would I make it possible to show the output (print it) from the console when running apply. Currently I get the following error message, although my types have derive Show
:
<interactive>:77:1: error:
* No instance for (Show (CMonad Value)) arising from a use of `print'
* In a stmt of an interactive GHCi command: print it
CodePudding user response:
The >>
operator is optional, not required. The documentation states that the minimal complete definition is >>=
. While you can implement both >>
and return
, you don't have to. If you don't supply them, Haskell can use default implementations that are derived from either >>
and/or Applicative
's pure
.
The type class definition is (current GHC source code, reduced to essentials):
class Applicative m => Monad m where
(>>=) :: forall a b. m a -> (a -> m b) -> m b
(>>) :: forall a b. m a -> m b -> m b
m >> k = m >>= \_ -> k
return :: a -> m a
return = pure
Notice that >>=
lacks an implementation, which means that you must supply it. The two other functions have a default implementation, but you can 'override' them if you want to.
If you want to see output from GHCi, the type must be a Show
instance. If the type wraps a function, there's no clear way to do that.
CodePudding user response:
The declaration of Monad
in the standard Prelude is as follows: (simplified from the Prelude source)
class Applicative m => Monad m where
(>>=) :: forall a b. m a -> (a -> m b) -> m b
(>>) :: forall a b. m a -> m b -> m b
m >> k = m >>= \_ -> k
{-# INLINE (>>) #-}
return :: a -> m a
return = pure
It's a typeclass with three methods, (>>=)
, (>>)
and return
.
Of those three, two have a default implementation - the function is implemented in the typeclass, and one does not.
Monad
is a subclass of Applicative
, and return
is the same as pure
- it is included in the Monad
typeclass (or at all) for historical reasons.
Of the remaining two, (>>=)
is all that is needed to define a Monad
in Haskell. (>>)
could be defined outside of the typeclass, like this:
(>>) :: (Monad m) => forall a b. m a -> m b -> m b
m >> k = m >>= \_ -> k
The reason this is included is in case a monad author wants to override the default implementation with an implementation which is more efficient.
A typeclass method which is not required is know as optional.
Haddock documentation automatically generates a 'Mnimal complete definition' based on the methods without default implementations. You can see here that the minimal definition of Monad
is indeed (>>=)
.
Sometimes, all methods can have default implementations, but they are not optional. This can happen when one of two methods must be provided, and the other is defined in terms of it. This is the case for the Traversable
typeclass, where traverse
and sequenceA
are both implemented in terms of each other. Not implementing either method will cause them to go into an infinite loop.
To let you mark this, GHC provides the MINIMAL
pragma, which generates the neccesary compiler warnings, and ensures the Haddocks are correct.
As an aside, failing to implement a required typeclass method is by default a compiler warning, not an error, and will cause a runtime exception if called. There is no good reason for this behaviour.
You can change this default using the -Werror=missing-methods
GHC flag.
Happy Haskelling!