{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE InstanceSigs #-} {-# LANGUAGE QuantifiedConstraints #-} {-# LANGUAGE MultiParamTypeClasses #-} import System.IO import System.Environment import Data.Char class MonadTrans t where lift :: (Monad m) => m a -> (t m) a -- t is monad transformer if -- for any monad m, we have a monad (t m) data Identity a = Id {runId :: a} instance Functor Identity where fmap f (Id x) = Id (f x) instance Applicative Identity where instance Monad Identity where return :: a -> Identity a return = Id (>>=) :: Identity a -> (a -> Identity b) -> Identity b (Id x) >>= f = f x type State s a = StateT s Identity a instance (Functor m) => Functor (StateT s m) where fmap :: (a -> b) -> StateT s m a -> StateT s m b fmap f (StateT g) = StateT $ \ x -> fmap (\ (y, s) -> (f y, s)) (g x) instance (Applicative m) => Applicative (StateT s m) where instance (Monad m) => Monad (StateT s m) where return :: a -> StateT s m a return x = StateT $ \ s -> return (x, s) -- :: m (a, s) (>>=) :: StateT s m a -> (a -> StateT s m b) -> StateT s m b (StateT g) >>= f = StateT $ \ x -> g x >>= (\ (y, z) -> let (StateT h) = f y in h z ) instance MonadTrans (StateT s) where lift :: (Monad m) => m a -> StateT s m a lift y = StateT $ \ s -> y >>= (\ x -> return (x, s)) parse :: String -> Parser a -> Maybe (a, String) parse s p = runStateT p s class Monad m => MonadState s m where get :: m s put :: s -> m () instance Monad m => MonadState s (StateT s m) where get :: (StateT s m) s get = StateT $ \ s -> return (s, s) -- :: m (s, s) put :: s -> (StateT s m) () put x = StateT $ \ s -> return ((), x) -- :: m ((), s) type Parser a = StateT String Maybe a eof :: Parser () eof = do s <- get if null (s :: String) then return () else mzero item :: Parser Char item = do s <- get case s of [] -> mzero x:xs -> do put xs return x -- Monoid of monads class Monad m => MonadPlus m where mzero :: m a mplus :: m a -> m a -> m a instance MonadPlus Maybe where mzero :: Maybe a mzero = Nothing mplus :: Maybe a -> Maybe a -> Maybe a Nothing `mplus` x = x Just x `mplus` y = Just x -- Our first example of a monad transformer. -- It gives us ability to add state to an existing -- monad. data StateT s m a = StateT {runStateT :: s -> m (a, s)} instance (MonadPlus m) => MonadPlus (StateT s m) where mzero :: StateT s m a mzero = StateT $ \ s -> mzero -- :: m (a, s) mplus :: StateT s m a -> StateT s m a -> StateT s m a (StateT g1) `mplus` (StateT g2) = StateT $ \ s -> g1 s `mplus` g2 s -- :: m (a, s) (<|>) :: Parser a -> Parser a -> Parser a (<|>) = mplus sat :: (Char -> Bool) -> Parser Char sat f = do x <- item if f x then return x else mzero -- Parse a charactor. char :: Char -> Parser Char char c = sat (\ x -> x == c) -- Recursive parser combinators. Parse an input string -- String = [Char] string :: String -> Parser String string [] = return [] string (x:xs) = do y <- char x ys <- string xs return (y:ys) -- Apply a parser zero or more times. many :: Parser a -> Parser [a] many p = do{ x <- p; xs <- many p; return (x:xs)} <|> return [] -- Apply a parser one or more times. many1 :: Parser a -> Parser [a] many1 p = do x <- p xs <- many p return (x:xs) -- sepBy p sep parses zero or more occurrences of p, -- separated by sep. Returns a list of values returned by p. sepby :: Parser a -> Parser b -> Parser [a] p `sepby` sep = do{ x <- p; xs <- many (sep >> p); return (x:xs) } <|> return [] sepby1 :: Parser a -> Parser b -> Parser [a] p `sepby1` sep = do{ x <- p; xs <- many (sep >> p); return (x:xs) } -- A simple CSV parser csv :: Parser [[String]] csv = do ls <- many line eof return ls line :: Parser [String] line = do cs <- content `sepby1` comma (char '\n' >> return ()) <|> eof return cs comma :: Parser Char comma = char ',' -- parse a charactor as long as it is not one of the charactor -- in the input string. noneOf :: [Char] -> Parser Char noneOf str = do c <- item if elem c str then mzero else return c content :: Parser String content = many1 (noneOf ",;.\n") -- Now let us make a simple command line utility that -- replace the comma with semi-colon. -- Some useful monadic IO functions: -- getArgs :: IO [String] -- putStrLn :: String -> IO () -- openFile :: FilePath -> IOMode -> IO Handle -- hGetContents :: Handle -> IO String -- hClose :: Handle -> IO () -- Some useful string processing functions. -- words, unwords, lines, unlines main :: IO () main = do args <- getArgs case args of [filename] -> do h <- openFile filename ReadMode str <- hGetContents h case parse str csv of Nothing -> putStrLn "failed to parse" Just (result, _) -> do -- result :: [[String]] putStrLn $ stringWithSep '\n' [stringWithSep ';' l | l <- result] hClose h _ -> putStrLn "wrong arguments" -- concatenate a list of string with a separator charactor. -- e..g, stringWithSep ';' ["hello", "world"] == hello;world stringWithSep :: Char -> [String] -> String stringWithSep sep [] = [] stringWithSep sep [x] = x stringWithSep sep (x:xs) = x ++ [sep] ++ stringWithSep sep xs -- remove the extra spaces inside a content rmSpaces :: String -> String rmSpaces = undefined