Syntax In Depth
View Source< Getting Started | Up: Introduction | Index | Quick Reference >
comp do blocks
comp do
x <- effect() # effectful bind — run effect, bind result
{:ok, y} = expr # pure pattern match (left of = is the pattern)
z = expr # pure assignment
value # last expression is auto-lifted
endAll expressions except the last must be <- or =. The final
expression is automatically lifted via Comp.pure/1.
<- bind
The <- operator runs an effectful computation and binds its result:
comp do
user <- Repo.get(User, id) # user is the unwrapped value
name <- Reader.ask() # reads from Reader handler
{user, name}
endDesugars to Comp.bind(effect(), fn result -> ... end).
Pattern matching with <-
comp do
{:ok, user} <- Repo.get(User, id) # match on result tuple
{:ok, count} = {:ok, 42} # pure pattern match (= not <-)
endUse <- for effectful operations, = for pure values.
else clause
Handle pattern match failures in <-:
comp do
{:ok, user} <- Repo.get(User, id)
else
{:error, :not_found} -> {:error, :not_found}
{:error, reason} -> Throw.throw(reason)
endcatch clause
Intercept effects or install handlers inline:
comp do
result <- risky_computation()
result
catch
{Throw, :not_found} -> :default_value # intercept specific error
State -> 0 # install handler (State.with_handler(0))
Reader -> %{timeout: 5000} # install handler (Reader.with_handler(...))
endThe {Effect, pattern} form intercepts the effect. The bare Effect
form installs a handler. First clause innermost, last outermost.
defcomp
Define a function that returns a computation:
defmodule MyApp.Users do
use Skuld.Syntax
defcomp get_user(id) do
{:ok, user} <- Repo.get(User, id)
{:ok, user}
end
endEquivalent to:
def get_user(id) do
comp do
{:ok, user} <- Repo.get(User, id)
{:ok, user}
end
endquery do blocks
A query block analyzes dependencies between deffetch calls and
batches independent fetches into concurrent round-trips:
defcomp load_dashboard(user_id) do
query do
user <- Users.get_user(user_id)
# these two are independent — batched together:
recent <- Posts.get_recent()
orders <- Orders.get_by_user(user.id)
{user, recent, orders}
end
enddefquery and defqueryp are query equivalents of defcomp/defcompp:
defquery user_with_orders(id) do
user <- Users.get_user(id)
orders <- Orders.get_by_user(user.id)
{user, orders}
end
defqueryp private_fetch(id) do
data <- DataSource.fetch(id)
data
endAll three (query, defquery, defqueryp) are imported by
use Skuld.Syntax. Requires a FiberPool.with_handler in the stack.
defcallback (with Port.EffectfulFacade)
Define a typed port operation:
defmodule MyApp.Users do
use Skuld.Effects.Port.EffectfulFacade
defcallback get_user(id :: String.t()) :: {:ok, User.t()} | {:error, term()}
endGenerates a function get_user/1 returning computation(User.t() | {:error, term()}).
Auto-lifting
Any expression that isn't a 2-arity function (a computation) is
automatically lifted as Comp.pure(value). This is what makes Skuld's
syntax work naturally in a dynamic language — you almost never need
to think about return types. Bare values, case results, if/else
branches, and any Plain Old Elixir expression all Just Work:
comp do
x <- State.get()
x * 2 # auto-lifted
end
comp do
x <- State.get()
_ <- if x > 5, do: Writer.tell(:big) # nil auto-lifted when false
x
end
comp do
x <- State.get()
msg = case x do
0 -> :zero # auto-lifted
_ -> :other # auto-lifted
end
msg
endThe one exception: if you want to return a function/2 value, wrap
it in Comp.pure/1. Otherwise the runtime sees a 2-arity function
and tries to invoke it as a computation:
comp do
Comp.pure(fn a, b -> a + b end) # explicit lift for function value
endRunning
Comp.run!(comp) # extract value, raises on Throw/Suspend
{result, env} = Comp.run(comp) # returns raw result + envHandling effects
Piping through with_handler:
comp
|> State.with_handler(0)
|> Reader.with_handler(%{})
|> Throw.with_handler()
|> Comp.run!()Handler order is generally independent (each manages its own effect).
The exception - effects which intercept other effects: EffectLogger
must be innermost to record all effects.
< Getting Started | Up: Introduction | Index | Quick Reference >