Home > Net >  Using >> without explicitly declaring it in a monad
Using >> without explicitly declaring it in a monad

Time:11-25

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!

  • Related