Skip to content

Instantly share code, notes, and snippets.

@fcard
Last active June 20, 2016 19:36
Show Gist options
  • Select an option

  • Save fcard/b48513108a32c13a49a387a3c530f7de to your computer and use it in GitHub Desktop.

Select an option

Save fcard/b48513108a32c13a49a387a3c530f7de to your computer and use it in GitHub Desktop.
Evil hack to implement partial application in Julia
# julia 0.5 only!
module GeneratedPartial
export @partialize, partialize, ?
const MAX_UNDERSCORE = 10
macro times(n,action)
:(Any[$action for _ in 1:$n])
end
mapquote(f, l) = quote $((map(f,l))...); end
abstract PartialComponent <: Function
type Underscore{N} <: PartialComponent
end
type PartialCall{F<:Function,N,S} <: PartialComponent
f::F
end
PartialCall{F}(f::F,n,s) = PartialCall{F,n,s}(f)
type ClosedPartialCall{P<:PartialCall} <: Function
p::P
end
type PartializedFunction{F} <: Function
f::F
end
(::Underscore{N}){N}(args...) = PartialCall(f -> f(args...), (N,), 1)
(f::PartialCall)(args...) = f.f(args...)
(f::ClosedPartialCall)(args...) = f.p(args...)
partial_group{N}(::Type{Underscore{N}}) = (N,)
partial_group{F,N,S}(::Type{PartialCall{F,N,S}}) = N
partial_group(x) = []
partial_call_group{F,N,S}(p::Type{PartialCall{F,N,S}}) = N
partial_call_sizes{F,N,S}(p::Type{PartialCall{F,N,S}}) = S
partial_call_group_size{F,N,S}(g::Int, p::Type{PartialCall{F,N,S}}) = S[findnext(N, g, 1)]
partial_size(x) = 1
partial_size{F,N,S}(p::Type{PartialCall{F,N,S}}) = sum(S)
is_int_iterable(ex::Expr) = ex.head in [:(:),:vect,:hcat,:vcat,:tuple] && all(x->(isa(x,Integer)), ex.args)
is_name(s::Symbol) = true
is_name(e::Expr) = (e.head == :quote && is_name(e.args[1])) || (e.head == :. && all(is_name, e.args))
is_name(q::QuoteNode) = is_name(q.value)
macro partialize(a::Integer,f...)
@assert all(is_name, f)
esc(m_partialize(a, f...))
end
macro partialize(a::Expr, f...)
@assert is_int_iterable(a)
@assert all(is_name, f)
esc(m_partialize(eval(a), f...))
end
function m_partialize(r::AbstractArray, f...)
mapquote(r) do n
m_partialize(n, f...)
end
end
function m_partialize(n::Int, f...)
names = @times(n,gensym())
rest = Expr(:..., gensym())
mapquote(f) do fn
mapquote(arglist_combinations(n,names,rest)) do args
:($fn($(args...)) = $partialcall($fn, $(names...), $rest))
end
end
end
function arglist_combinations(n,names,rest)
map(1:2^n-1) do i
s = bin(i,n)
a = map(eachindex(s)) do si
t = s[si]=='0'? Any : PartialComponent
:($(names[si])::$t)
end
push!(a,rest)
a
end
end
partialize(f) = PartializedFunction(f)
@generated function (f::PartializedFunction)(args...)
if any(x->x<:PartialComponent, args)
:($partialcall(f,args...))
else
:(f.f(args...))
end
end
@generated function partialcall(f::Function, args...)
partialcall_impl(f,args)
end
function partialcall_impl(f, args)
groups, preargs = group_partials(args)
names = argument_names(args, groups)
anon=foldl(final_call_expr(length(args), names, preargs, groups), reverse(eachindex(groups))) do e, g
add_new_layer(e,g,groups,names)
end
:($PartialCall($anon, $(usedgroups(groups)), $(argsize(groups))))
end
function argsize(groups)
used = usedgroups(groups)
ntuple(length(used)) do i
foldl(0, map(j->groups[j], used[i])) do n, tinfo
t = tinfo[1]
n + (t <: Underscore? 1 : partial_call_group_size(used[i],t))
end
end
end
function usedgroups(groups)
tuple(find(x->!isempty(x), groups)...)
end
function argument_names(preargs, groups)
n = length(preargs)
for g in eachindex(groups)
for (t,i,j) in groups[g]
if t <: PartialCall
n += partial_call_group_size(g,t)
else
n += 1
end
end
end
@times n gensym()
end
function add_new_layer(expr, g, groups, names)
isempty(groups[g]) && return expr
args = []
for (t,i,j) in groups[g]
if t <: Underscore
push!(args, names[j])
else
append!(args, names[j:(j+partial_call_group_size(g,t)-1)])
end
end
:(($(args...),) -> $expr)
end
function final_call_expr(n, names, preargs, groups)
args = Array(Any,n)
defn = falses(n)
for i in preargs
args[i] = :(args[$i])
end
for g in eachindex(groups)
for (t,i,j) in groups[g]
if t <: PartialCall
if !defn[i]
args[i] = :(args[$i])
defn[i] = true
end
s = partial_call_group_size(g,t)
args[i] = :($(args[i])($(names[j:j+s-1]...)))
else
args[i] = names[j]
end
end
end
:(f($(args...)))
end
function maxgroup(args)
pcomps = filter(x->x<:PartialComponent, args)
maximum(map(x->maximum(partial_group(x)), pcomps))
end
function group_partials(args)
groups = @times maxgroup(args) []
preargs = Int[]
group_partials!(groups, preargs, args)
groups, preargs
end
function group_partials!(g, p, args)
j = 1
for i in eachindex(args)
t = args[i]
n = partial_group(args[i])
if n == []
push!(p, i)
else
for ni in eachindex(n)
push!(g[n[ni]], [t,i,j+ni-1])
end
end
j += partial_size(t)
end
end
for n in 1:MAX_UNDERSCORE
underscore = Symbol("_"^n)
@eval begin
const $underscore = $Underscore{$n}()
export $underscore
end
end
?(p::PartialCall) = ClosedPartialCall(p)
Base.:*(::typeof(?), p::PartialCall) = ClosedPartialCall(p)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment