Skip to content

Instantly share code, notes, and snippets.

@agocorona
Last active August 30, 2025 00:31
Show Gist options
  • Save agocorona/f3ef8a85b1d55646433fe38ca647f88c to your computer and use it in GitHub Desktop.
Save agocorona/f3ef8a85b1d55646433fe38ca647f88c to your computer and use it in GitHub Desktop.

transient Primitives Cheatsheet

This cheatsheet summarizes the key primitives of the transient and transient-universe libraries in Haskell, with concise explanations and examples.

1. Execution and Main Control Primitives

Primitive Explanation Example
keep Initializes and keeps an interactive TransIO application running. It executes the main computation and a command-line interpreter (REPL). It terminates when the computation produces an exit event (e.g., exit).

main = keep $ do
liftIO $ putStrLn "¡Hola desde Transient!"
(option "greet" "Says hello" >> liftIO (putStrLn "¡Hola!"))
keep' Initializes and executes a TransIO computation for parallel processing. It does not include the console REPL. It waits for all generated threads to finish and returns a list with their results.

main = do
results <- keep' $ do
r <- async (return "Task 1")<|> async (return "Task 2")
liftIO $ print $ r ++ " completed"
option Declares a persistent command in the console REPL. The associated computation is activated each time the user invokes the command. It acts as a persistent event producer.

menu = (option "add" "Add a new task" >> liftIO (putStrLn "Adding...")) <|>
(option "list" "List tasks" >> liftIO (putStrLn "Listing..."))
main = keep menu
option1 Declares a command in the console REPL that can only be invoked once. After its first use, the command is no longer available.

main = keep $ do
option1 "init" "Initialize system" >> liftIO (putStrLn "System initialized.")
liftIO $ putStrLn "The 'init' option is no longer available."
input Prompts the user for input in the console, reads it, and validates it. It is typically used after an option.

main = keep $ do
option "num" "Enter a number < 10" >> do
n <- input (<10) "Number? > "
liftIO $ print $ "You entered: " ++ show n
<|> Alternative operator. If the left computation fails (produces empty or stop), the right computation is executed. It allows defining alternative execution paths or combining event streams.

main = keep $ do
(option "a" "Option A" >> liftIO (putStrLn "You chose A")) <|>
(option "b" "Option B" >> liftIO (putStrLn "You chose B"))

2. Concurrency and Parallelism Primitives

Primitive Explanation Example
abduce Forks the computation. The rest of the do block is executed in a new thread, while for the current thread, abduce acts as stop (fails). It is the basis for building other concurrency primitives.

main = keep $ do
(do
liftIO $ putStrLn "Main Thread: Before abduce"
abduce
liftIO $ putStrLn "Child Thread: After abduce"
) <|>
(do
liftIO $ putStrLn "Main Thread: Alternative path"
)
liftIO $ putStrLn "Both threads: End of block"
async Executes an IO action in a new background thread and returns its result to the TransIO flow when it finishes. Built on abduce.

main = keep $ do
liftIO $ putStrLn "Starting asynchronous task..."
result <- async $ do
liftIO $ threadDelay 2000000 -- Simulate work
return "Task completed"
liftIO $ putStrLn $ "Result received: " ++ result
for Processes a list of elements sequentially in a single thread. Although the loop body can launch asynchronous tasks, for schedules each iteration in order. Equivalent to threads 0 . choose.

main = keep $ do
n <- for [1..3 :: Int]
liftIO $ print n
choose Processes a list of elements in parallel, attempting to execute each element in a separate thread. The order of execution is not guaranteed.

main = keep $ threads 2 $ do
-- Only 2 threads will be used for printing
n <- choose [1..5 :: Int]
liftIO $ print n
threads Limits the maximum number of threads that can be created for a computation. threads 0 forces sequential execution in a single thread.

main = keep $ threads 1 $ do
-- Only one print will execute at a time
n <- choose [1..5 :: Int]
liftIO $ print n
await Waits for a TransIO computation (and all child threads it has generated) to complete fully, collecting all results into a list.

main = keep $ do
results <- await $
(async $ return "A") <|>
(async $ return "B")
liftIO $ print results -- Prints ["A", "B"] or ["B", "A"]
<*> Applicative operator. Executes the left and right computations in parallel and combines their results.

main = keep $ do
res <- (,) <$> (async $ return "Hello") <*> (async $ return "World")
liftIO $ print res -- Prints ("Hello", "World")
<> Semigroup operator. Executes the left and right computations in parallel and concatenates their results (if the type allows it).

main = keep $ do
res <- (async $ return "Part1") <> (async $ return "Part2")
liftIO $ putStrLn res -- Prints "Part1Part2"
+ (Num) Overloaded arithmetic operator for TransIO. Executes the computations in parallel and sums their results.

main = keep $ do
res <- (async $ return 5) + (async $ return 3)
liftIO $ print res -- Prints 8

3. Control Flow and Error Handling Primitives

Primitive Explanation Example
onException Registers a handler for Haskell exceptions (SomeException). The handler executes when an exception occurs within the computation's scope. Handlers are chained and execute in reverse order of their definition.

main = keep $ do
onException $ \e -> liftIO $ print $ "Error caught: " ++ show e
liftIO $ fail "Something went wrong!"
forward Stops the propagation of a specific type of backtracking event (e.g., an exception, an undo event) and resumes normal execution. Used within handlers (onBack, onException, etc.) to signal that the event has been handled and no further handlers for that type should be executed.

main = keep $ do
onException $ \e -> do
liftIO $ putStrLn "Handler: Error handled, stopping propagation."
forward e -- Stops further exception propagation
liftIO $ putStrLn "Program executes this forever."
liftIO $ fail "Failure"
continue A specialized version of forward used within exception handlers (onException). It stops the propagation of the exception backtracking and resumes normal program execution from that point.

main = keep $ do
(do
onException $ (e :: IOException) -> do
liftIO $ putStrLn "File not found, creating default."
-- Logic to create file
continue -- Stops exception propagation and resumes normal execution
liftIO $ readFile "non_existent.txt")
liftIO $ putStrLn "The program continues here."
onUndo Registers a handler for "undo" or "cancel" events. It executes when an undo event is triggered.

main = keep $ do
onUndo $ liftIO $ putStrLn "Operation undone. Cleaning..."
option "cancel" "Cancel operation" >> undo
liftIO $ putStrLn "Performing long operation..."
undo Triggers an "undo" event, activating registered onUndo handlers. (See onUndo example)
onFinish Registers a handler that executes when a computation and all its child threads have completed fully (either by success, failure, or cancellation). It guarantees resource cleanup.

main = keep $ do
onFinish $ const $ liftIO $ putStrLn "Resources released."
liftIO $ putStrLn "Starting task..."
-- ... task that may fail or complete ...
liftIO $ putStrLn "Task finished."
finish Triggers a completion event. It is usually done by the transient runtime automatically when a computation branch finishes. (Normally not called directly, it's internal)

4. Distributed Computing Primitives (transient-universe)

Primitive Explanation Example
local Executes a TransIO computation on the current node. Its result is "logged" so it can be "teleported" to other nodes.

main = keep $ initNode $ do
local $ liftIO $ putStrLn "This message only on my node."
runAt Executes a computation on a specific remote node. The computation is "teleported" to the remote node, executed there, and the result is returned to the original node.

-- Assuming 'remoteNode' is a Node
main = keep $ initNode $ do
res <- runAt remoteNode $ local $ return "Hola desde el remoto"
liftIO $ putStrLn res
atRemote Within a wormhole or onBrowser context, it indicates that the nested computation should be executed on the remote node (server) instead of the local node (client).

-- Conceptual example in a web context
webFib = onBrowser $ do
-- This executes in the browser
local . render $ h1 "Click for Fibonacci"
-- This executes on the server
fibNum <- atRemote $ do
n <- local . choose $ take 1 fibs
return $ show n
-- This executes back in the browser
local . render $ h2 fibNum
clustered Executes a computation on all nodes in the cluster in parallel. Results are collected on the node that initiated the call.

main = keep $ initNode $ do
-- Assuming other nodes are connected
messages <- clustered $ local $ do
myNode <- getMyNode
return $ "Hello from " ++ show myNode
liftIO $ print messages
teleport The low-level primitive for moving execution. It sends the remaining computation log to the remote node and stops local execution. (Normally not called directly, it's internal to runAt, atRemote, etc.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment