Last active
June 20, 2016 19:36
-
-
Save fcard/b48513108a32c13a49a387a3c530f7de to your computer and use it in GitHub Desktop.
Evil hack to implement partial application in Julia
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # 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