[Haskell-beginners] Monadic composition without throwing genericity under the bus?

Stephen Tetley stephen.tetley at gmail.com
Thu Feb 4 05:35:50 EST 2010


Hi Dave

You might need more that a type system extension for this one.

I've called your composition operator (*>>*), if you stack the type
signatures together I can't see a way of getting from (*>>*) to (.).

Compare it to the Kleisli composition of monads - where the step to
(.) is more apparent (swap '-> m' for cat):

-- (.) :: cat b c -> cat a b -> cat a c
-- (.) :: b `cat` c -> a `cat` b -> a `cat` c       -- infix

-- (*>>*) :: Monad m => m (b -> c) -> m (a -> b) -> m (a -> c)

-- Kleisli composition does satisfy Category
-- _Kleisli_ :: Monad m => (b -> m c) -> (a -> m b) -> (a -> m c)


I think I've derived what you are doing below with Graham Hutton's
parser monad, the 'easy work around' is to use (*>>*) rather than try
to use (.):

module Fun where



-- Starting with a Graham Hutton's parser as it is (probably) the
-- simplest monadic parser

newtype Parser a = Parser { parse :: (String -> [(a,String)]) }

instance Monad Parser where
  return a = Parser (\cs -> [(a,cs)])
  p >>= f  = Parser (\cs -> concat [parse (f a) cs' | (a,cs') <- parse p cs])


item :: Parser Char
item =  Parser (\cs -> case cs of
                        ""     -> []
                        (c:cs) -> [(c,cs)])

zero :: Parser a
zero = Parser (\cs -> [])

sat :: (Char -> Bool) -> Parser Char
sat p = do {c <- item; if p c then return c else zero}


char0 :: Char -> Parser Char
char0 c = sat (c==)

char :: Char -> Parser ShowS
char c = do {ch <- sat (c==) ; return (showChar c) }

-- consume but don't produce ?
place0 :: String -> Parser String
place0 str = do { str' <- mapM char0 str; return "" }

place :: String -> Parser ShowS
place str = place0 str >> return (\s -> s)       -- no string aka id

string0 :: String -> Parser String
string0 str = mapM char0 str

string :: String -> Parser ShowS
string str = do { str' <- string0 str; return (showString str) }

--------------------------------------------------------------------------------

dot :: Parser ShowS
dot = char '.'

comment :: Parser ShowS
comment = place "-- "

runParser :: Parser ShowS -> String -> String
runParser p inp = post $ ((parse p) inp)
  where
    post [(ans,_)] = ans $ ""
    post xs        = error (unlines $ map (\(f,cs) -> show (f "",cs)) xs)

demo1 :: String
demo1 = runParser (comment >> dot) "-- ."


startPragma :: Parser ShowS
startPragma = string "{-#"


demo2 :: String
demo2 = runParser (startPragma) "{-#"

space :: Parser ShowS
space = char ' '


languageU :: Parser ShowS
languageU = string "LANGUAGE"

-- first attempt - 'endo' style keeps the same type
(*>*) :: Monad m => m (a -> a) -> m (a -> a) -> m (a -> a)
mf *>* mg = do { f <- mf; g <- mg; return (f.g) }


demo3 :: String
demo3 = runParser (startPragma *>* space *>* languageU) "{-# LANGUAGE"


-- second attempt - 'bluebird' style - proper composition
-- (more general)
(*>>*) :: Monad m => m (b -> c) -> m (a -> b) -> m (a -> c)
mf *>>* mg = do { f <- mf; g <- mg; return (f.g) }


demo4 :: String
demo4 = runParser (startPragma *>>* space *>>* languageU) "{-# LANGUAGE"

-- (.) :: cat b c -> cat a b -> cat a c
-- (*>>*) :: Monad m => m (b -> c) -> m (a -> b) -> m (a -> c)

-- ???

-- Kleisli composition does satisfy Category
-- _Kleisli_ :: Monad m => (b -> m c) -> (a -> m b) -> (a -> m c)

-- TYPE ERROR
-- demo5x :: String
-- demo5x = runParser (startPragma . space . languageU) "{-# LANGUAGE"


More information about the Beginners mailing list