Skip to content

Instantly share code, notes, and snippets.

@zachallaun
Created April 11, 2013 20:20
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save zachallaun/5366855 to your computer and use it in GitHub Desktop.
Save zachallaun/5366855 to your computer and use it in GitHub Desktop.
# time
# ====
macro time(ex)
quote
local t0 = time_ns()
local val = $(esc(ex))
local t1 = time_ns()
println("elapsed time: ", (t1-t0)/1e9, " seconds")
val
end
end
# let*
# ====
function make_let_star(body, bindings)
if isempty(bindings)
body
else
sym, exp = bindings[1].args
:(let $sym = $exp
$(make_let_star(body, bindings[2:]))
end)
end
end
macro star(letex)
body, bindings = letex.args[1], letex.args[2:]
make_let_star(body, bindings)
end
# bindto
# ======
macro bindto(sym, ifex)
if is(ifex.head, :if)
sym = esc(sym)
quote
local $sym
$(transform_ifex(sym, ifex))
end
else
error("@bindto must be passed a symbol and an if expression")
end
end
function transform_ifex(sym, ifex)
# assuming an else clause
pred, thenex, elseex = ifex.args
elseex = elseex.args[2]
elseex = elseex.head == :if ? transform_ifex(sym, elseex) : esc(elseex)
quote
$sym = $(esc(pred))
if $sym != false
$(esc(thenex))
else
$elseex
end
end
end
maybe_inc(n::Int) = n == 0 ? false : n + 1
@bindto x if maybe_inc(0)
println("never")
elseif maybe_inc(1)
println("success: $x")
else
println("fail")
end
begin
local x
x = maybe_inc(0)
if x != false
println("never")
else
x = maybe_inc(1)
if x != false
println("success: $x")
else
println("fail")
end
end
end
# anaphoric bind
# ==============
macro abind(ifex)
:(@bindto $(esc(:it)) $ifex)
end
# multimethods
# ===========
# What do we want our macro to look like?
# =======================================
# @multi numbercrunch(x::Int, y::Int) = (x >= 0, y >= 0)
# @method function numbercrunch(x::Int, y::Int)::(true, true)
# x + y
# end
# @method function numbercrunch(x::Int, y::Int)::(true, false)
# x - y
# end
# @method function numbercrunch(x::Int, y::Int)::(false, true)
# x / y
# end
# @method function numbercrunch(x::Int, y::Int)::(false, false)
# x * y
# end
# How could we implement it without macros?
# =========================================
# numbercrunch = multidispatch((x::Int, y::Int) -> (x >= 0, y >= 0))
# addmethod!(numbercrunch, (true, true), function (x::Int, y::Int)
# x + y
# end)
# const dispatchtable = Dict{Function, Dict{Any, Function}}()
# function multidispatch(fn::Function)
# methodtable = Dict{Any, Function}()
# function dispatchfn(args...)
# val = fn(args...)
# @bindto method if get(methodtable, val, false)
# method(args...)
# else
# error("No dispatch function for dispatch value $val")
# end
# end
# dispatchtable[dispatchfn] = methodtable
# dispatchfn
# end
# function addmethod!(dispatchfn::Function, val, method::Function)
# @bindto methodtable if get(dispatchtable, dispatchfn, false)
# methodtable[val] = method
# else
# error("$dispatchfn is not a multimethod dispatch function")
# end
# end
# Macroify
# ========
const dispatchtable = Dict{Function, Dict{Any, Function}}()
macro multi(ex)
name = ex.args[1].args[1]
name = isa(name, Symbol) ? esc(name) : esc(name.args[1])
params = tuple(ex.args[1].args[2:end]...)
args = map((x) -> isa(x, Symbol) ? x : x.args[1], params)
pred = ex.args[2]
@gensym method
quote
methodtable = Dict{Any, Function}()
function $name($(params...))
val = $pred
@bindto $method if get(methodtable, val, false)
$(Expr(:call, method, args...))
else
error("No implementation for dispatch value $val")
end
end
dispatchtable[$name] = methodtable
$name
end
end
function addmethod!(dispatchfn::Function, val, method::Function)
@bindto methodtable if get(dispatchtable, dispatchfn, false)
methodtable[val] = method
else
error("$dispatchfn is not a multimethod dispatch function")
end
dispatchfn
end
macro method(ex)
is(ex.head, :function) || error("@method only supports `function` syntax")
# Capture and remove the name from the function expression
name = esc(ex.args[1].args[1])
ex.args[1] = Expr(:tuple, ex.args[1].args[2:end]...)
# Capture and remove the dispatch value
dispatchval = esc(ex.args[2].args[2].args[1])
delete!(ex.args[2].args, 1:2)
:(addmethod!($name, $dispatchval, $(esc(ex))))
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment