BoundCond
bound_cond/1 — a cond that lets you thread interim variables through its
clauses.
Plain cond runs the first truthy clause's body, but each clause is isolated: a
value you compute while testing one condition can't be reused by the next. withcan thread state, but it models a single happy path with fallbacks — not several
branches of equal priority. bound_cond fills that gap.
import BoundCond
bound_cond do
in_range?( x, y) ->
n
:bind ->
last = get_last( x)
pos = get_pos( y)
pos > last ->
last
true ->
pos
end
Clauses are tried top to bottom, exactly like cond. A :bind -> clause never
matches — its body runs only when execution falls through to it, and the
variables it binds are in scope for every clause below it. You can use as many
:bind -> steps as you like, and each sees the ones before it. As with cond,
nothing bound inside leaks out of the bound_cond. If no clause matches,
bound_cond raises CondClauseError, just like cond.
It expands to nested if/else wrapped in a case (used only to scope the
bindings), so there is effectively no runtime overhead — the macro is purely a
way to keep the source flat and declarative instead of hand-rolling nested ifs.
Installation
Add bound_cond to your dependencies in mix.exs:
def deps do
[
{ :bound_cond, "~> 0.1.0"}
]
end
Then run mix deps.get. The API reference lives on
HexDocs.
How it works
Once a do block contains any ->, Elixir parses it in stab-clause mode, where
every non-arrow line is folded into the preceding clause's body. So free-floating
bindings between clauses aren't possible; instead, a binding step is written as a
clause with the reserved head :bind, and the macro splices that body into the
fall-through path of the if/else chain it builds.
Test
mix test
License
Released under the MIT License — see LICENSE.
Copyright (c) 2026 DaTrader.