Slide 1

Slide 1 text

Free Monads Getting Started

Slide 2

Slide 2 text

Self-introduction /laʒenɔʁɛ̃k/ カマイルカ lagénorhynque (defprofile lagénorhynque :name "Kent OHASHI" :account @lagenorhynque :company "Opt, Inc." :languages [Clojure Haskell Python Scala English français Deutsch русский] :interests [programming language-learning mathematics] :contributing [github.com/japan-clojurians/clojure-site-ja])

Slide 3

Slide 3 text

Contents 1. What are monads? 2. What are Free monads? 3. Example: RPN expressions

Slide 4

Slide 4 text

What are monads?

Slide 5

Slide 5 text

de nition of Monad cf. GHC.Base#Monad class Applicative m => Monad m where (>>=) :: forall a b. m a -> (a -> m b) -> m b (>>) :: forall a b. m a -> m b -> m b m >> k = m >>= \_ -> k {-# INLINE (>>) #-} return :: a -> m a return = pure fail :: String -> m a fail s = errorWithoutStackTrace s scalaz/Monad.scala

Slide 6

Slide 6 text

return 値 a をモナド m に⼊れる Haskell: return Scalaz: point a -> m a A => M[A]

Slide 7

Slide 7 text

bind 値 m a に 関数 a -> m b を適⽤して m b にする Haskell: >>= Scalaz: bind (cf. flatMap) m a -> (a -> m b) -> m b M[A] => (A => M[B]) => M[B]

Slide 8

Slide 8 text

do notation (Haskell) ↓ desugar a = Just 2 b = Just 3 c = do x <- a y <- b return $ x * y a = Just 2 b = Just 3 c = a >>= (\x -> b >>= (\y -> return $ x * y))

Slide 9

Slide 9 text

for expression (Scala) ↓ desugar val a = Some(2) val b = Some(3) val c = for { x <- a y <- b } yield x * y val a = Some(2) val b = Some(3) val c = a.flatMap { x => b.map { y => x * y } }

Slide 10

Slide 10 text

What are Free monads?

Slide 11

Slide 11 text

de nition of Free cf. Control/Monad/Free.hs data Free f a = Pure a | Free (f (Free f a)) instance Functor f => Monad (Free f) where return = pure {-# INLINE return #-} Pure a >>= f = f a Free m >>= f = Free ((>>= f) <$> m) scalaz/Free.scala

Slide 12

Slide 12 text

リストのような再帰的データ構造 data [] a = [] | a : [a] f が Functor ⇒ Free f は Monad → Functor のインスタンスを Free で包めば Monad として扱える ※ GHC拡張で Functor を⾃動導出することも: DeriveFunctor

Slide 13

Slide 13 text

Example: RPN expressions

Slide 14

Slide 14 text

repositories cf. lagenorhynque/freemonad-hs FreeMonad/RPNExpr.hs lagenorhynque/freemonad-scala freemonad/RPNExpr.scala

Slide 15

Slide 15 text

RPN: Reverse Polish Notation ↓ with parentheses ↓ evaluate 逆ポーランド記法 - Wikipedia 8 6 1 - * 2 + ((8 (6 1 -) *) 2 +) 42

Slide 16

Slide 16 text

DSL interpreter RPN DSL = AST -- Free monad → eval :: AST -> Num → stringify :: AST -> String ← parse :: String -> AST

Slide 17

Slide 17 text

RPNのASTを表現するデータ型 RPNExpr を定義する data RPNExpr n expr = Number n expr | Add expr | Sub expr | Mul expr | End deriving (Show)

Slide 18

Slide 18 text

RPNExpr を Functor にする instance Functor (RPNExpr n) where fmap f (Number n expr) = Number n $ f expr fmap f (Add expr) = Add $ f expr fmap f (Sub expr) = Sub $ f expr fmap f (Mul expr) = Mul $ f expr fmap _ End = End

Slide 19

Slide 19 text

RPNExpr を Free で包んで RPN Monad として扱う type RPN a b = Free (RPNExpr a) b liftF :: (Functor f) => f r -> Free f r liftF = Free . fmap Pure num :: a -> RPN a () num n = liftF $ Number n () add :: RPN a () add = liftF $ Add () sub :: RPN a () sub = liftF $ Sub () mul :: RPN a () mul = liftF $ Mul () end :: RPN a b end = liftF End

Slide 20

Slide 20 text

RPN モナド(RPNのDSL = AST)を試してみる > :{ | expr1 :: RPN Double () | expr1 = do | num 8 | num 6 | num 1 | sub | mul | :} > expr1 Free (Number 8.0 (Free (Number 6.0 (Free (Number 1.0 (Free (Sub (Free (Mul (Pure ()))))))))))

Slide 21

Slide 21 text

複数の式を連結してみる > :{ | expr2 :: RPN Double () | expr2 = do | num 2 | add | end | :} > expr2 Free (Number 2.0 (Free (Add (Free End)))) > expr1 >> expr2 Free (Number 8.0 (Free (Number 6.0 (Free (Number 1.0 (Free (Sub (Free (Mul (Free (Number 2.0 (Free (Add (Free End)))))))))))))) > :t expr1 >> expr2 expr1 >> expr2 :: Free (RPNExpr Double) () -- RPN Double ()

Slide 22

Slide 22 text

RPNのASTを⽂字列化する関数 stringify stringify :: (Show a) => RPN a b -> String stringify (Free (Number n e)) = show n ++ " " ++ stringify e stringify (Free (Add e)) = "+ " ++ stringify e stringify (Free (Sub e)) = "- " ++ stringify e stringify (Free (Mul e)) = "* " ++ stringify e stringify (Free End) = "." stringify (Pure _) = ""

Slide 23

Slide 23 text

> expr1 Free (Number 8.0 (Free (Number 6.0 (Free (Number 1.0 (Free (Sub (Free (Mul (Pure ())))))))))) > stringify expr1 "8.0 6.0 1.0 - * " > expr2 Free (Number 2.0 (Free (Add (Free End)))) > stringify expr2 "2.0 + ." > stringify $ expr1 >> expr2 "8.0 6.0 1.0 - * 2.0 + ."

Slide 24

Slide 24 text

⽂字列をRPNのASTに変換する関数 parse parse :: (Read a) => String -> Either String (RPN a ()) parse = foldM rpn (Pure ()) . reverse . words where rpn e "+" = Right . Free $ Add e rpn e "-" = Right . Free $ Sub e rpn e "*" = Right . Free $ Mul e rpn _ "." = Right $ Free End rpn e n = case reads n of [(v,_)] -> Right . Free $ Number v e _ -> Left "invalid input"

Slide 25

Slide 25 text

> parse "8.0 6.0 1.0 - * " :: Either String (RPN Double ()) Right (Free (Number 8.0 (Free (Number 6.0 (Free (Number 1.0 (Free (Sub (Free (Mul (Pure ()))))))))))) > parse "2.0 + ." :: Either String (RPN Double ()) Right (Free (Number 2.0 (Free (Add (Free End))))) > parse "8.0 6.0 1.0 - * 2.0 + ." :: Either String (RPN Double ()) Right (Free (Number 8.0 (Free (Number 6.0 (Free (Number 1.0 (Free (Sub (Free (Mul (Free (Number 2.0 (Free (Add (Free End))))))))))))))) > parse "2.0 3.0 /" :: Either String (RPN Double ()) Left "invalid input"

Slide 26

Slide 26 text

RPNのASTを評価する関数 eval eval :: (Num a) => RPN a b -> Either String a eval = calc [] where calc stack (Free (Number n e)) = calc (n : stack) e calc (n1:n2:ns) (Free (Add e)) = calc (n2 + n1 : ns) e calc (n1:n2:ns) (Free (Sub e)) = calc (n2 - n1 : ns) e calc (n1:n2:ns) (Free (Mul e)) = calc (n2 * n1 : ns) e calc (n:_) (Free End) = Right n calc (n:_) (Pure _) = Right n calc _ _ = Left "invalid expression"

Slide 27

Slide 27 text

> expr1 Free (Number 8.0 (Free (Number 6.0 (Free (Number 1.0 (Free (Sub (Free (Mul (Pure ())))))))))) > eval expr1 Right 40.0 > expr2 Free (Number 2.0 (Free (Add (Free End)))) > eval expr2 Left "invalid expression" > eval $ expr1 >> expr2 Right 42.0

Slide 28

Slide 28 text

データ型を定義して Functor にすることで、 Free を介して Monad が得られた 例えば データ型として定義したASTをモナドに ⇒ 合成可能なDSL(= モナド)が提供できる ⇒ AST(= モナド)を解釈する関数群を⽤意する ことでインタプリタが書ける

Slide 29

Slide 29 text

Further Reading / 第13章 外部作⽤とI/O Haskell for all: Why free monads matter 独習 Scalaz Free Monad 猫番 ⾃由モナド (Free) 『Scala関数型デザイン&プログラミング』 Functional Programming in Scala

Slide 30

Slide 30 text

free: Monads for free Control/Monad/Free.hs scalaz/scalaz scalaz/Free.scala typelevel/cats cats/free/Free.scala

Slide 31

Slide 31 text

/ 10.1 逆ポーランド記法電卓 14.6 安全な逆ポーランド記法電卓を作ろう cf. Interpreter パターン - Wikipedia 『すごいHaskellたのしく学ぼう!』 Learn You a Haskell for Great Good! MP in Scala MP in Haskell Functor, Applicative, Monadのシンプルな定式化