Extensible Effects

Effectful Computations Composable

Matej Kollár
github.com/xkollar

2017-02-22

Programs that do something

main :: IO ()
main = do
    putStrLn "Hello, what is your name?"
    name <- readLine
    putStrLn ("Hello " <> name <> "!")

Monads

Monad Transformers

newtype ProtocolT m a = ProtocolT (ReaderT Request (WriterT Response (MaybeT m)) a)
  deriving (Applicative, Functor, Monad)

:-(

Extensible Effects

type Protocol = '[Reader Request, Writer Response, Exc ()]
action :: Members Protocol effs => Eff effs a
--  action :: Monad m => ProtocolT m a

Console

data Console s where
    PutStrLn :: String -> Console ()
    GetLine :: Console String

putStrLn' :: Member Console r => String -> Eff r ()
putStrLn' = send . PutStrLn

getLine' :: Member Console r => Eff r String
getLine' = send GetLine
hello :: Member Console effs => Eff effs ()
hello = do
    putStrLn' "Hello, what is your name?"
    name <- readLine'
    putStrLn' ("Hello " <> name <> "!")

Handlers

runConsoleIO :: Member IO r => Eff (Console ': r) w -> Eff r w
main :: IO ()
main =     runM (runConsoleIO hello)
--         |     |            |
--  IO () -'     |            |
-- Eff '[IO] () -'            |
--     Eff '[Console, IO] () -'
runConsolePure :: [String] -> Eff (Console ': r) a
    -> Eff r (a, ([String], [String]))
    -- ^ (value, (unconsumed input, produced output))

Use in Other Languages

Conclusion

Questions?

References

Haskell

Other Languages

Original paper

Haskell

Problems With Monad Transformers

Examples

exampleEcho :: Member Console r => Eff r ()
exampleEcho = forever $ do
    putStrLn' "Send me something to echo..."
    s <- getLine'
    putStrLn' ("Thanks for sending " ++ show s)

mainEcho :: IO ()
mainEcho = runM (runConsoleM exampleEcho)
--         |     |            |
--  IO () -'     |            |
-- Eff '[IO] () -'            |
--    Eff '[Console, IO] () -'

Capitalization as a Service

data Capitalize v where
    Capitalize :: String -> Capitalize String

capitalize :: Member Capitalize r => String -> Eff r String
capitalize = send . Capitalize

runCapitalizeM :: Eff (Capitalize ': r) w -> Eff r w

Composing

exampleCapitalize :: Members '[Console, Capitalize] effs => Eff effs ()
exampleCapitalize = forever $ do
    putStrLn' "Send me something to capitalize..."
    l <- getLine'
    when (null l) exitSuccess'
    capitalize l >>= putStrLn'
mainCapitalize1 :: IO ()
mainCapitalize1 = runM (runConsoleM (runCapitalizeM exampleCapitalize))
--                |     |             |              |
--         IO () -'     |             |              |
--        Eff '[IO] () -'             |              |
--             Eff '[Console, IO] () -'              |
--                Eff '[Capitalize, Console, IO] () -'
mainCapitalize2 :: IO ()
mainCapitalize2 = runM (runCapitalizeM (runConsoleM exampleCapitalize))
--                |     |             |              |
--         IO () -'     |             |              |
--        Eff '[IO] () -'             |              |
--          Eff '[Capitalize, IO] () -'              |
--                Eff '[Console, Capitalize, IO] () -'

Extra notes

Instances for Eff

Effects Readily available