Skip to content

Commit

Permalink
Merge pull request #184 from martinholters/functorize
Browse files Browse the repository at this point in the history
Add @functorize
  • Loading branch information
tkelman committed Apr 22, 2016
2 parents cc3f69b + 2aacc82 commit 195de1a
Show file tree
Hide file tree
Showing 3 changed files with 166 additions and 0 deletions.
2 changes: 2 additions & 0 deletions README.md
Expand Up @@ -160,6 +160,8 @@ Currently, the `@compat` macro supports the following syntaxes:

* `@inline` and `@noinline` have been added. On 0.3, these are "no-ops," meaning they don't actually do anything.

* `@functorize` (not present in any Julia version) takes a function (or operator) and turns it into a functor object if one is available in the used Julia version. E.g. something like `mapreduce(Base.AbsFun(), Base.MulFun(), x)` can now be written as `mapreduce(@functorize(abs), @functorize(*), x)` to work accross different Julia versions.

## Other changes

* `Dict(ks, vs)` is now `Dict(zip(ks, vs))` [#8521](https://github.com/JuliaLang/julia/pull/8521)
Expand Down
77 changes: 77 additions & 0 deletions src/Compat.jl
Expand Up @@ -980,4 +980,81 @@ if !isdefined(Base, :istextmime)
istextmime(m::@compat(Union{MIME,AbstractString})) = istext(m)
end

export @functorize
macro functorize(f)
if VERSION >= v"0.5.0-dev+3701"
f === :scalarmax ? :(Base.scalarmax) :
f === :scalarmin ? :(Base.scalarmin) :
f === :centralizedabs2fun ? :(typeof(Base.centralizedabs2fun(0)).name.primary) :
f
else
f = f === :identity ? :(Base.IdFun()) :
f === :abs ? :(Base.AbsFun()) :
f === :abs2 ? :(Base.Abs2Fun()) :
f === :exp ? :(Base.ExpFun()) :
f === :log ? :(Base.LogFun()) :
f === :& ? :(Base.AndFun()) :
f === :| ? :(Base.OrFun()) :
f === :+ ? :(Base.AddFun()) :
f === :* ? :(Base.MulFun()) :
f === :scalarmax ? :(Base.MaxFun()) :
f === :scalarmin ? :(Base.MinFun()) :
f === :centralizedabs2fun ? :(Base.CentralizedAbs2Fun) :
f
if VERSION >= v"0.4.0-dev+4902"
f = f === :< ? :(Base.LessFun()) :
f === :> ? :(Base.MoreFun()) :
f
end
if VERSION >= v"0.4.0-dev+4902"
f = f === :conj ? :(Base.ConjFun()) :
f
end
if VERSION >= v"0.4.0-dev+6254"
f = f === :- ? :(Base.SubFun()) :
f === :^ ? :(Base.PowFun()) :
f
end
if VERSION >= v"0.4.0-dev+6256"
f = f === :/ ? :(Base.RDivFun()) :
f === :\ ? :(Base.LDivFun()) :
f === :div ? :(Base.IDivFun()) :
f
end
if VERSION >= v"0.4.0-dev+6353"
f = f === :$ ? :(Base.XorFun()) :
f === :.+ ? :(Base.DotAddFun()) :
f === :.- ? :(Base.DotSubFun()) :
f === :.* ? :(Base.DotMulFun()) :
f === :mod ? :(Base.ModFun()) :
f === :rem ? :(Base.RemFun()) :
# DotRemFun is defined, but ::call(::DotRemFun, ...) is not until later
#f === :.% ? :(Base.DotRemFun()) :
f === :.<< ? :(Base.DotLSFun()) :
f === :.>> ? :(Base.DotRSFun()) :
f
end
if VERSION >= v"0.4.0-dev+6359"
f = f === :./ ? :(Base.DotRDivFun()) :
f
end
if VERSION >= v"0.4.0-rc1+59"
f = f === :max ? :(Base.ElementwiseMaxFun()) :
f === :min ? :(Base.ElementwiseMinFun()) :
f
end
if VERSION >= v"0.5.0-dev+741"
f = f === :complex ? :(Base.SparseArrays.ComplexFun()) :
f === :dot ? :(Base.SparseArrays.DotFun()) :
f
end
if VERSION >= v"0.5.0-dev+1472"
f = f === symbol("") ? :(Base.DotIDivFun()) :
f === :.% ? :(Base.DotRemFun()) :
f
end
f
end
end

end # module
87 changes: 87 additions & 0 deletions test/runtests.jl
Expand Up @@ -1004,3 +1004,90 @@ if VERSION < v"0.4.0"
else
@compat rc = RemoteChannel{Channel{Any}}(1,2,3)
end

# @functorize
function checkfunc(Fun, func)
if VERSION >= v"0.5.0-dev+3701"
@eval @test @functorize($(func)) === Base.$(func)
else
if isdefined(Base, Fun)
@eval @test isa(@functorize($(func)), Base.$(Fun))
else
@eval @test isa(@functorize($(func)), Function)
@eval @test @functorize($(func)) === Base.$(func)
end
end
end

for (Fun, func) in [(:IdFun, :identity),
(:AbsFun, :abs),
(:Abs2Fun, :abs2),
(:ExpFun, :exp),
(:LogFun, :log),
(:ConjFun, :conj)]
begin
if isdefined(Base, func)
checkfunc(Fun, func)
a = rand(1:10, 10)
@eval @test mapreduce($(func), +, $(a)) == mapreduce(@functorize($(func)), +, $(a))
end
end
end

for (Fun, func) in [(:AndFun, :&),
(:OrFun, :|),
(:XorFun, :$),
(:AddFun, :+),
(:DotAddFun, :.+),
(:SubFun, :-),
(:DotSubFun, :.-),
(:MulFun, :*),
(:DotMulFun, :.*),
(:RDivFun, :/),
(:DotRDivFun, :./),
(:LDivFun, :\),
(:IDivFun, :div),
(:DotIDivFun, symbol("")),
(:ModFun, :mod),
(:RemFun, :rem),
(:DotRemFun, :.%),
(:PowFun, :^),
(:MaxFun, :scalarmax),
(:MinFun, :scalarmin),
(:LessFun, :<),
(:MoreFun, :>),
(:DotLSFun, :.<<),
(:DotRSFun, :.>>),
(:ElementwiseMaxFun, :max),
(:ElementwiseMinFun, :min)]
begin
if isdefined(Base, func) && (func !== :.>> || VERSION >= v"0.4.0-dev+553") && (func !== :.% || VERSION >= v"0.5.0-dev+1472")
checkfunc(Fun, func)
a = rand(1:10, 10)
@eval @test mapreduce(identity, Base.$(func), $(a)) == mapreduce(identity, @functorize($(func)), $(a))
end
end
end

if VERSION >= v"0.5.0-dev+3701"
@test @functorize(complex) === complex
@test @functorize(dot) === dot
else
if isdefined(Base, :SparseArrays) && isdefined(Base.SparseArrays, :ComplexFun)
@test isa(@functorize(complex), Base.SparseArrays.ComplexFun)
@test isa(@functorize(dot), Base.SparseArrays.DotFun)
else
@test isa(@functorize(complex), Function)
@test isa(@functorize(dot), Function)
@test @functorize(complex) === complex
@test @functorize(dot) === dot
end
end
let a = rand(1:10, 10)
@test mapreduce(identity, dot, a) == mapreduce(identity, @functorize(dot), a)
end
@test isa(@functorize(centralizedabs2fun)(1), @functorize(centralizedabs2fun))
@test isa(@functorize(centralizedabs2fun)(1.0), @functorize(centralizedabs2fun))
let a = rand(1:10, 10)
@eval @test mapreduce(x -> abs2(x - 1), +, $(a)) == mapreduce(@functorize(centralizedabs2fun)(1), +, $(a))
end

0 comments on commit 195de1a

Please sign in to comment.