function that’s pure. By the time we return the stick to another effect, we MUST have performed the actual log. • log :: String -> stick -> stick • We need • log :: String -> stick -> IO stick
just need to specify that downstream effects have to wait before using the stick, until the logging effect is complete. • We need to hand off the stick, but still control access.
won’t have enough money in the bank until tomorrow • Instead of money (INR), give them a (post- dated) cheque! • Cheque INR. “Almost as good as INR”. • Cheque is the “context” around the INR that is handed off to the next person.
we need to log two things, there’s no point in waiting until the “first cheque is cashed” (log done), before even “writing a cheque” for the second log. • \stick -> let stick2 = log “Hello ” stick log “World” stick2
“free” to play a trick • log :: String -> stick -> [m] stick • Let’s call the context “Log” and reify into a type. • data Log stick where GADT Syntax Log :: String -> stick -> Log stick • data Log stick = Log String stick
add a “Join” constructor - • data Log a where Log :: String -> a -> Log a Join :: Log (Log a) -> Log a • Join (Log “Hello ” (Log “World” ())) :: Log () • It’s a data structure. E.g. we could serialise this to JSON - { tag: “Join” , val: {tag: “Log”, str: “Hello”, stick: {{tag: “Log”, str: “World”, stick: {}}}} }
Log :: String -> a -> LogF a • type Log a = Join LogF a • data ConsoleF a where PrintLine :: String -> a -> ConsoleF a ReadLine :: (String -> a) -> ConsoleF a • type Console a = Join ConsoleF a
a) = return a runConsole (Join (ReadLine reader)) = do s <- getLine runConsole (reader s) runConsole (Join (PrintLine s next)) = do putStrLn s runConsole next
a) runLogF (Log s rest) = do log s return rest • runConsoleF :: Fold ConsoleF a (IO a) runConsole(ReadLine reader) = do s <- getLine return (reader s) runConsole (PrintLine s next) = do putStrLn s return next