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
return(value) # explicit lift (optional)
value # last expression is auto-lifted
end<- 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
enddefcallback (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
Bare values at the end of a comp block are automatically lifted:
comp do
x <- State.get()
x * 2 # auto-lifted — no return() needed
endif without else also auto-lifts:
comp do
x <- State.get()
_ <- if x > 5, do: Writer.tell(:big) # nil auto-lifted when false
x
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 independent (each manages its own effect). EffectLogger must be innermost to record all effects.
< Getting Started | Up: Introduction | Index | Quick Reference >