rebit
rebit

Reputation: 45

Problem with pure definition in Applicative

I'm a Haskell beginner and I've to solve a worksheet question and this time I'm really stuck. Help!:))

I've to work out the instances for some monad starting with functor up to monad and i can't figure out the pure in the Applicative instance. I don't want to post all the code of the program but I think thoe following should illustrate the problem:

This is given code, can't be changed:

class (Semigroup a, Monoid a) => Log a where
    logMsg :: String -> a

newtype Logger l a = Logger (a,l)
    deriving Show

logMsgM :: Log l => String -> Logger l ()
logMsgM s = Logger ((), logMsg s)

These are the instances I'm working on:

instance (Log l) => Functor (Logger l) where 
    fmap g (Logger (a, l)) = Logger ((g a), l)


instance (Log l) => Applicative (Logger l) where 
    pure ???????????????????
    (<*>) (Logger (g,l)) (Logger (a,_)) = Logger ((g a), l)

instance (Log l) => Monad (Logger l) where 
    return                                              = pure
    (>>=) (Logger (a,l)) g                              = (g a)
    (>>)  x y                                           = x >>= \_ -> y

With the pure I'm stuck. pure = Logger produces infinte type error a ~ (a,l) and all attempts to tweak with input paranmeters end up in either unknown variable error, or strange type errors with type variables with tailing zeros and such stuff. I tried so much, that's difficult to post the error messages in a more sensible way. I don't undertand what's going on. This is based on a introduction lecture, so I know about the basic concepts. I just can't handle this newtype definition correctly with the function declaration (I think...), in particular this subsumption of two parameters into one (pair). I do understand (or maybe better wording: "I can follow") the typical examples with cases in type defintions or newtypes, such as maybe , either, or other with same number or more parameters at the right side. However, maybe, I'm wrong with this assumption and I'm making a fallacy somewhere I don't see at all.

Btw, the whole code compiles when writing pure = return, but the program produces a stack overflow (smile). I think this is no wonder as return = puer and pure = return may well compile but makes no sense, right!?

Thanx for help :)

Upvotes: 3

Views: 168

Answers (1)

bradrn
bradrn

Reputation: 8467

Logger is a type more commonly known as the Writer monad. The idea is that if you have a Logger l a, with l being a Semigroup, then if you run a Logger l a and then another Logger l b, the result will have the two ls next to each other (i.e. concatenated using the Monoid operation). That is:

Logger (1,"test1") >> Logger (2,"test2")   ==   Logger (2,"test1" <> "test2")

Given this, pure a will just be Logger (a, something), where something is a value which doesn’t have any effect when concatenated. But now look at the typeclass declarations:

class (Semigroup a, Monoid a) => Log a where ...
instance (Log l) => Applicative (Logger l) where ...

So in Applicative (Logger l), l must be a Monoid, so it must have an identity value mempty! This gives us pure a = Logger (a, mempty), which simply returns a without affecting the log.


However, there is still a problem. If we have Logger (a1,l1) >>= \x -> Logger (a2,l2), or Logger (a1,l1) <*> Logger (a2,l2), we want to concatenate l1 <> l2 in the result. But your current implementation doesn’t do that! So you’ll need to change it to satisfy this property. Since I don’t want to solve the whole worksheet for you, I’m leaving this as an exercise; however, if you become stuck, you may want to consult the link at the top of this answer.

Upvotes: 6

Related Questions