Skip to content

Instantly share code, notes, and snippets.

@MikeInnes
Last active August 29, 2015 14:02
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MikeInnes/668c5c7cdd8fd8b81d35 to your computer and use it in GitHub Desktop.
Save MikeInnes/668c5c7cdd8fd8b81d35 to your computer and use it in GitHub Desktop.
isexpr(x::Expr, ts...) = x.head in ts
isexpr{T}(x::T, ts...) = T in ts
macro with(exprs...)
exprs = collect(exprs)
target = nothing
if length(exprs) > 1 && !isexpr(exprs[end], :(::))
target = esc(exprs[end])
pop!(exprs)
end
@assert all(e->isexpr(e, :(::)), exprs)
prebindings = Expr[]
postbindings = Expr[]
for dec in exprs
obj = dec.args[1] |> esc
t = dec.args[2]
@assert isexpr(t, :tuple, Symbol)
vs = isexpr(t, :tuple) ? t.args : names(eval(t))
for v in vs
field, var = v, esc(v)
push!(prebindings, :($var = $obj.$field))
push!(postbindings, :($obj.$field = $var))
end
end
if target != nothing
block = quote
result = $target
$(postbindings...)
result
end
Expr(:let, block, prebindings...)
else
Expr(:block, prebindings...)
end
end
type Foo; a; b; end
f = Foo(1, 1)
# By default, simply binds all variable names
let
@with f::Foo
a, b = b, a+b
end
# This version modifies f.a – evaluate it repeatedly to get a fibonacci sequence
@with f::Foo begin
a, b = b, a+b
end
# Alternate syntax: explicitly declare fields to be used
let
@with f::(a, b)
a = a + b
end
# Multiple objects are also supported with
@with obj1::T obj2::S
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment