Search code examples
haskellfunctional-programmingmonadsstate-monad

rewriting a monad with newtype in haskell - some clarifications


I have come across the code below and thought I wanted to rewrite it in order to get a firmer grasp of the Monad. As I have tried to do below. It raises several questions about aspects that I apparently haven't understood fully.

type R = Int
type W = String
type S = Double

newtype RWSP a = RWSP {runRWSP :: R -> S ->
                                    (a, W, S)}

instance Monad RWSP where
  return a = RWSP(\_ state -> (a, mempty, state))  
  m >>= f = RWSP(\r state -> 
    let (a, w, state') = runRWSP m r state
        (a', w', state'') = runRWSP (f a) r state' 
    in (a', w++w', state''))


instance Functor RWSP where
  fmap = liftM
instance Applicative RWSP where
  pure = return; (<*>) = ap
newtype CustomM a = CustomM {runCustomM :: a -> String}

instance Monad CustomM where
  return a = CustomM (\a -> mempty)
  m >>= f =  CustomM (\a -> 
             let str = runCustomM m
                 str' = runCustomM (f a)
             in (str' ++ str)) 


instance Functor CustomM where 
  fmap = liftM 
instance Applicative CustomM where 
  pure = return; (<*>) = ap

1a. Why is the runRWSP needed in the original example after the let statement. Does this have to do with that a runRWSP takes a monad m, whereas the RWSP is a monad in itself with the type a which requires an anonymous function?

1b. In that case could one go about solving the original code by rewriting it so that no runRWSP was needed but rather a RWSP was used instead?

2a. What about the several nested tuples in the let expression. Why are both important. The f a seems to surround whatever the first runRWSP m r state returns and I guess we can only assume that this monad takes an r and an s due to its newtype definition, is that correctly? When I try to do something similar for the edited version I seem to get an error.

bc. Furthermore I suspect you can use the m as an argument for runRWSP due to the reverse nature of newtype like you would with: ghci> CharList "tdada" CharList {getCharList = "tdada"} ghci> getCharList (CharList "something") "something" at http://learnyouahaskell.com/functors-applicative-functors-and-monoids#the-newtype-keyword

3a. Is it correct to say that the bind operator needs the runRWSP constructor on the right side, because the bind operator has as its output m b which would require a function whereas the RWSP a is a monad and not a function and therefore does not change the packed value a to b

4a. How can I rewrite the monad correctly such that it will compile correctly with the new data type?


Solution

  • Why is the runRWSP needed in the original example after the let statement.

    It is one of the two available, canonical ways of unwrapping the newtype wrapper and accessing the chewy nougat inside. The other would be pattern matching; e.g. you could write

    let (a, w, state') = case m of RWSP f -> f r state
    

    instead. It would mean the same thing.

    Could one go about solving the original code by rewriting it so that no runRWSP was needed but rather a RWSP was used instead?

    Yes. See above.

    What about the several nested tuples in the let expression. Why are both important?

    There are no nested tuples in your code. This one:

        let (a, w, state') = runRWSP m r state
    

    is important because:

            (a', w', state'') = runRWSP (f a) r state'
                                           ^    ^^^^^^
        in (a', w++w', state''))
                ^
    

    This one:

            (a', w', state'') = runRWSP (f a) r state' 
    

    is important because:

        in (a', w++w', state''))
            ^^     ^^  ^^^^^^^
    

    The f a seems to surround whatever the first runRWSP m r state returns and I guess we can only assume that this monad takes an r and an s due to its newtype definition, is that correctly?

    Sure. I don't love the exact phrasing, but I think what you intended to say here is correct.

    Is it correct to say that the bind operator needs the runRWSP constructor on the right side, because the bind operator has as its output m b which would require a function whereas the RWSP a is a monad and not a function and therefore does not change the packed value a to b

    No, for many reasons. runRWSP is not a constructor, and it's not needed on the right hand side as described in the first question. m b usually is not specialized to be a function type, even when Monad m is floating around in the context somewhere; moreover, in this instance declaration, the m b that appears in the class declaration has already been specialized to something that is not a function type (namely, RWSP b). RWSP a is not a monad; RWSP (with no application to a) together with its implementations of fmap, return, and (>>=) is a monad.

    How can I rewrite the monad correctly such that it will compile correctly with the new data type?

    It cannot be done. Your type parameter appears in a negative position; in any correct Monad implementation, the type parameter appears only in positive positions.

    Or, in less technical (but also less precise) terms: your data type consumes/accepts/absorbs values of its parameter type, whereas monadic values produce/provide/contain values of the parameter type.