Control.MonadPlus.Freeはいなくなりました
えにっきをみてください
freeの新バージョンにControl.MonadPlus.Freeなるものがあったのでさっそく使ってみた。MonadPlus版Freeのコンストラクタは、Pure、Freeに加えて新たにPlusが加わっており、任意のFunctorからMonadPlusなモナドを生成することができる。
以下は、Freeモナドによって錬成されたパーサコンビネータの例。
import Control.Monad import Control.MonadPlus.Free import Control.Applicative import Data.Char data ParseStep a = ParseStep (Maybe Char -> Maybe a) instance Functor ParseStep where fmap f (ParseStep g) = ParseStep (fmap f . g) type Parser = Free ParseStep runParser :: Parser a -> String -> Maybe a runParser (Pure a) "" = Just a runParser (Pure _) _ = Nothing runParser (Free (ParseStep f)) str = case str of [] -> f Nothing >>= flip runParser [] (x:xs) -> f (Just x) >>= flip runParser xs runParser (Plus xs) str = msum $ map (flip runParser str) xs -- ここがポイント! anyChar :: Parser Char anyChar = liftF (ParseStep id) satisfy :: (Char -> Bool) -> Parser Char satisfy f = do ch <- anyChar if f ch then pure ch else empty char :: Char -> Parser Char char ch = satisfy (==ch) string :: String -> Parser String string str = mapM char str digit :: Parser Int digit = digitToInt <$> satisfy isDigit natural :: Parser Int natural = foldl ((+) . (*10)) 0 <$> some digit parens :: Parser a -> Parser a parens p = char '(' *> p <* char ')' chainl :: Parser a -> Parser (a -> a -> a) -> Parser a chainl p op = p >>= rest where rest x = (op <*> pure x <*> p >>= rest) <|> return x spaces :: Parser a -> Parser a spaces p = many (char ' ') *> p <* many (char ' ') fact :: Parser Int fact = parens expr <|> natural term :: Parser Int term = chainl fact $ spaces $ char '*' *> return (*) <|> char '/' *> return div expr :: Parser Int expr = chainl term $ spaces $ char '+' *> return (+) <|> char '-' *> return (-) main = do let ev = runParser expr print $ ev "42" -- Just 42 print $ ev "1 + 1" -- Just 2 print $ ev "1 - (2 - 3)" -- Just 2 print $ ev "(1 - 2) - 3" -- Just (-4) print $ ev "1 - 2 - 3" -- Just (-4) print $ ev "2 * (3 + 5)" -- Just 16 print $ ev "(5 * 6 - 10) / 4" -- Just 5 print $ ev "1 + " -- Nothing
やっぱりFreeモナドはすごい。