{-# LANGUAGE TypeOperators, FlexibleContexts, Rank2Types #-} {-| Description : Examples of Control.Ev.Eff Copyright : (c) 2020, Microsoft Research; Daan Leijen; Ningning Xie License : MIT Maintainer : xnning@hku.hk; daan@microsoft.com Stability : Experimental Examples from /"Effect Handlers in Haskell, Evidently"/, Ningning Xie and Daan Leijen, Haskell 2020. -} module Examples where import Control.Ev.Eff import Prelude hiding (flip) import Data.Char import Data.Maybe -- BEGIN:reader data Reader a e ans = Reader { ask :: Op () a e ans } -- END:reader -- BEGIN:readerhr hr :: a -> Reader a e ans hr x = Reader{ ask = operation (\ () k -> k x) } -- END:readerhr -- BEGIN:readerh reader :: a -> Eff (Reader a :* e) ans -> Eff e ans reader x action = handler (hr x) action -- END:readerh -- when to introduce function -- show type of: handler :: h e ans -> Eff (h :* e) -> Eff e -- BEGIN:readerex1 sample1 = reader "world" $ do s <- perform ask () return ("hello " ++ s) -- END:readerex1 -- BEGIN:readermult greetOrExit::(Reader String :? e, Reader Bool :? e) => Eff e String greetOrExit = do s <- perform ask () isExit <- perform ask () if isExit then return ("goodbye " ++ s) else return ("hello " ++ s) -- END:readermult -- BEGIN:readernoctx greetMaybe :: (Reader String :? e) => Eff e (Maybe String) greetMaybe = do s <- perform ask () if null s then return Nothing else return (Just ("hello " ++ s)) -- END:readernoctx -- BEGIN:readergreet greet :: (Reader String :? e) => Eff e String greet = do s <- perform ask () return ("hello " ++ s) -- END:readergreet -- BEGIN:readerex helloWorld :: Eff e String helloWorld = reader "world" greet -- END:readerex -- BEGIN:exn data Exn e ans = Exn { failure :: forall a. Op () a e ans } -- END:exn -- BEGIN:toMaybe toMaybe :: Eff (Exn :* e) a -> Eff e (Maybe a) toMaybe = handlerRet Just $ Exn{ failure = operation (\ () _ -> return Nothing) } -- END:toMaybe -- BEGIN:exceptDefault exceptDefault :: a -> Eff (Exn :* e) a -> Eff e a exceptDefault x = handler $ Exn{ failure = operation (\ () _ -> return x) } -- END:exceptDefault -- BEGIN:exnex safeDiv :: (Exn :? e) => Int -> Int -> Eff e Int safeDiv x 0 = perform failure () safeDiv x y = return (x `div` y) -- END:exnex safeHead :: (Exn :? e) => String -> Eff e Char safeHead [] = perform failure () safeHead (x:_) = return x sample3 = reader "" $ toMaybe $ do s <- perform ask () c <- safeHead s return (Just c) -- introduce handlerRet -- BEGIN:state data State a e ans = State { get :: Op () a e ans , put :: Op a () e ans } -- END:state -- BEGIN:statex state :: a -> Eff (State a :* e) ans -> Eff e ans state init = handlerLocal init $ State{ get = function (\ () -> perform lget ()) , put = function (\ x -> perform lput x) } -- END:statex -- BEGIN:stateex add :: (State Int :? e) => Int -> Eff e () add i = do j <- perform get () perform put (i + j) -- END:stateex -- BEGIN:invert invert :: (State Bool :? e) => Eff e Bool invert = do b <- perform get () perform put (not b) perform get () -- END:invert -- BEGIN:double test :: Eff e Bool test = state True $ do invert b <- perform get () return b -- END:double adder = state (1::Int) $ do add 41 i <- perform get () return ("the final state is: " ++ show (i::Int)) -- BEGIN:output data Output e ans = Output { out :: Op String () e ans } output :: Eff (Output :* e) ans -> Eff e (ans,String) output = handlerLocalRet [] (\x ss -> (x,concat ss)) $ Output { out = function (\x -> localModify (x:)) } -- END:output -- BEGIN:amb data Amb e ans = Amb { flip :: Op () Bool e ans } -- END:amb -- BEGIN:xor xor :: (Amb :? e) => Eff e Bool xor = do x <- perform flip () y <- perform flip () return ((x && not y) || (not x && y)) -- END:xor -- BEGIN:allresults allResults :: Eff (Amb :* e) a -> Eff e [a] allResults = handlerRet (\x -> [x]) (Amb{ flip = operation (\ () k -> do xs <- k True ys <- k False return (xs ++ ys)) }) -- END:allresults -- BEGIN:backtrack firstResult :: Eff (Amb :* e) (Maybe a) -> Eff e (Maybe a) firstResult = handler Amb{ flip = operation (\ () k -> do xs <- k True case xs of Just _ -> return xs Nothing -> k False) } -- END:backtrack -- BEGIN:solutions solutions :: Eff (Exn :* Amb :* e) a -> Eff e [a] solutions action = fmap catMaybes (allResults (toMaybe action)) -- END:solutions -- BEGIN:eager eager :: Eff (Exn :* Amb :* e) a -> Eff e (Maybe a) eager action = firstResult (toMaybe action) -- END:eager -- BEGIN:choice choice :: (Amb :? e) => Eff e a -> Eff e a -> Eff e a choice p1 p2 = do b <- perform flip () if b then p1 else p2 -- END:choice -- BEGIN:manyeg many :: (Amb :? e) => Eff e a -> Eff e [a] many p = choice (many1 p) (return []) many1 :: (Amb :? e) => Eff e a -> Eff e [a] many1 p = do x <- p; xs <- many p; return (x:xs) -- END:manyeg -- BEGIN:parse data Parse e ans = Parse { satisfy :: forall a. Op (String -> (Maybe (a, String))) a e ans } -- END:parse -- BEGIN:parsefun parse :: (Exn :? e) => String -> Eff (Parse :* e) b -> Eff e (b, String) parse input = handlerLocalRet input (\x s -> (x, s)) $ Parse { satisfy = operation $ \p k -> do input <- perform lget () case (p input) of Nothing -> perform failure () Just (x, rest) -> do perform lput rest k x } -- END:parsefun -- BEGIN:symbol symbol :: (Parse :? e) => Char -> Eff e Char symbol c = perform satisfy (\input -> case input of (d:rest) | d == c -> Just (c, rest) _ -> Nothing) digit :: (Parse :? e) => Eff e Int digit = perform satisfy (\input -> case input of (d:rest) | isDigit d -> Just (digitToInt d, rest) _ -> Nothing) -- END:symbol -- BEGIN:expr expr :: (Parse :? e, Amb :? e) => Eff e Int expr = choice (do i <- term; symbol '+'; j <- term return (i + j)) term term :: (Parse :? e, Amb :? e) => Eff e Int term = choice (do i <- factor; symbol '*'; j <- factor return (i * j)) factor factor :: (Parse :? e, Amb :? e) => Eff e Int factor = choice (do symbol '('; i <- expr; symbol ')' return i) number number :: (Parse :? e, Amb :? e) => Eff e Int number = do xs <- many1 digit return $ foldl (\n d -> 10 * n + d) 0 xs -- END:expr test1 = runEff (solutions (parse "1+2*3" expr)) -- [(7,""),(3,"*3"),(1,"+2*3")] test2 = runEff (eager (parse "1+2*3" expr)) -- Just (7,"") -- BEGIN:evil data Evil e ans = Evil { evil :: Op () () e ans } hevil :: Eff (Evil :* e) a -> Eff e (() -> Eff e a) hevil = handlerRet (\x -> (\_ -> return x)) (Evil{ evil = operation (\_ k -> return (\_ -> do f <- k (); f ())) }) -- END:evil -- BEGIN:evilbody ebody :: (Reader Int :? e, Evil :? e) => Eff e Int ebody = do x <- perform ask () -- x == 1 perform evil () y <- perform ask () -- y == 2 ! return (x+y) -- END:evilbody -- BEGIN:nonscoped nonscoped :: Eff e Int nonscoped = do f <- reader (1::Int) (hevil ebody) reader (2::Int) (f ()) -- END:nonscoped