I'm deeply satisfied to be building an interaction tree runtime where the interaction trees are themselves computed via and represented by trees. It's trees all the way down.
87 lines
3.1 KiB
Plaintext
87 lines
3.1 KiB
Plaintext
!import "../lib/base.tri" !Local
|
|
!import "../lib/list.tri" !Local
|
|
!import "../lib/io.tri" !Local
|
|
|
|
-- Monadic IO in tricu
|
|
--
|
|
-- The IO system is a free monad interpreted by the host. Primitive actions
|
|
-- (putStr, readFile, writeFile, ...) do not carry their own continuations.
|
|
-- Sequencing is performed by the single generic `bind` constructor.
|
|
--
|
|
-- pure x -- lift a pure value into IO
|
|
-- bind action k -- run action, then apply k to its result
|
|
-- thenIO a b -- run a, discard its result, then run b
|
|
-- mapIO action f -- run action, then apply f to its result inside pure
|
|
--
|
|
-- File operations return a Result tree (see lib/base.tri):
|
|
-- ok value -- pair true (pair value t)
|
|
-- err code -- pair false (pair code t)
|
|
--
|
|
-- Use onReadFile / onWriteFile for convenient branching.
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- Example 1: Greet and return a pure value.
|
|
-- putStrLn writes to stdout; pure lifts "done" into IO.
|
|
-- ----------------------------------------------------------------------------
|
|
|
|
greet = (name :
|
|
bind (putStrLn (append "Hello, " name))
|
|
(_ : pure "done"))
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- Example 2: Read a file safely.
|
|
-- readFile returns a Result. matchResult branches on ok / err.
|
|
-- ----------------------------------------------------------------------------
|
|
|
|
safeRead = (path :
|
|
bind (readFile path)
|
|
(result :
|
|
matchResult
|
|
(err rest : pure "missing")
|
|
(contents rest : pure contents)
|
|
result))
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- Example 3: Write, then read back.
|
|
-- thenIO discards the writeFile Result and continues.
|
|
-- ----------------------------------------------------------------------------
|
|
|
|
writeThenRead = (path text :
|
|
thenIO
|
|
(writeFile path text)
|
|
(readFile path))
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- Example 4: Transform an IO result.
|
|
-- mapIO applies a pure function to the value produced by an action.
|
|
-- ----------------------------------------------------------------------------
|
|
|
|
shout = (path :
|
|
mapIO (safeRead path)
|
|
(text : append text "!!!"))
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- Example 5: Cooperative async.
|
|
-- fork runs an action in the background.
|
|
-- sleep suspends the current task for N milliseconds.
|
|
-- await waits for a forked task and returns its value.
|
|
--
|
|
-- Here the child sleeps for 2 s while the parent prints immediately.
|
|
-- The parent's message appears first, proving interleaving.
|
|
-- ----------------------------------------------------------------------------
|
|
|
|
asyncDemo = (
|
|
bind (fork
|
|
(bind (sleep 2000) (_ :
|
|
bind (putStrLn "Done sleeping!") (_ :
|
|
pure "child done"))))
|
|
(handle :
|
|
bind (putStrLn "Parent first!") (_ :
|
|
await handle)))
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- Main action - run the async demo.
|
|
-- ----------------------------------------------------------------------------
|
|
|
|
main = io asyncDemo
|