0

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?

asked Sep 21, 2022 at 13:09
3
  • 1
    This is far too broad; your "question" consists of at least 6 separate questions. Commented Sep 21, 2022 at 13:17
  • Ok i can rewrite it so that I shall focus only on 4a Commented Sep 21, 2022 at 13:23
  • The instances can be derived newtype RWSP a = RWSP {runRWSP :: R -> S -> (a, S, W)} deriving (Functor, Applicative, Monad, MonadFix, MonadReader R, MonadState S, MonadWriter W, MonadRWS R W S) via RWS R W S. You just need to swap the order of W and S. Commented Sep 21, 2022 at 15:03

1 Answer 1

2

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.

answered Sep 21, 2022 at 13:38
Sign up to request clarification or add additional context in comments.

2 Comments

Nice answer, so will it help if I formulate the newtype as newtype CustomM a = CustomM {runCustomM :: String -> (a, String)} because this one takes an argument S and produces a tupple with an additional 'contained' type a. Whereas my former newtype was expecting a 'contained' argument a. Is that what you mean by positive and negative positions respectively.
@Piskator Yes, that CustomM could support a Monad instance. (In fact, there are several distinct, lawful instances for isomorphic types in common use.) I explain positive/negative here; start with the paragraph marked "Edit:".

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.