Skip to content

Commit 726124e

Browse files
committed
Add @functorize
The new macro @functorize(f) turns function f into a Functor object if there is one in Base corresponding to f. E.g. @functorize(+) yields Base.AddFun(); @functorize(<) yields < in Julia 0.3, but Base.LessFun() in 0.4.
1 parent e71e7f2 commit 726124e

File tree

3 files changed

+144
-0
lines changed

3 files changed

+144
-0
lines changed

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,8 @@ Currently, the `@compat` macro supports the following syntaxes:
160160

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

163+
* `@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.
164+
163165
## Other changes
164166

165167
* `Dict(ks, vs)` is now `Dict(zip(ks, vs))` [#8521](https://github.com/JuliaLang/julia/pull/8521)

src/Compat.jl

+76
Original file line numberDiff line numberDiff line change
@@ -977,4 +977,80 @@ if !isdefined(Base, :istextmime)
977977
istextmime(m::@compat(Union{MIME,AbstractString})) = istext(m)
978978
end
979979

980+
export @functorize
981+
macro functorize(f)
982+
# TODO replace with proper condition on version once Julia#15804 is merged
983+
if VERSION >= v"0.5.0-" && !isdefined(Base, :BitFunctorUnary)
984+
f === :scalarmax ? :(Base.scalarmax) :
985+
f === :scalarmin ? :(Base.scalarmin) :
986+
f
987+
else
988+
f = f === :identity ? :(Base.IdFun()) :
989+
f === :abs ? :(Base.AbsFun()) :
990+
f === :abs2 ? :(Base.Abs2Fun()) :
991+
f === :exp ? :(Base.ExpFun()) :
992+
f === :log ? :(Base.LogFun()) :
993+
f === :& ? :(Base.AndFun()) :
994+
f === :| ? :(Base.OrFun()) :
995+
f === :+ ? :(Base.AddFun()) :
996+
f === :* ? :(Base.MulFun()) :
997+
f === :scalarmax ? :(Base.MaxFun()) :
998+
f === :scalarmin ? :(Base.MinFun()) :
999+
f
1000+
if VERSION >= v"0.4.0-dev+4902"
1001+
f = f === :< ? :(Base.LessFun()) :
1002+
f === :> ? :(Base.MoreFun()) :
1003+
f
1004+
end
1005+
if VERSION >= v"0.4.0-dev+4902"
1006+
f = f === :conj ? :(Base.ConjFun()) :
1007+
f
1008+
end
1009+
if VERSION >= v"0.4.0-dev+6254"
1010+
f = f === :- ? :(Base.SubFun()) :
1011+
f === :^ ? :(Base.PowFun()) :
1012+
f
1013+
end
1014+
if VERSION >= v"0.4.0-dev+6256"
1015+
f = f === :/ ? :(Base.RDivFun()) :
1016+
f === :\ ? :(Base.LDivFun()) :
1017+
f === :div ? :(Base.IDivFun()) :
1018+
f
1019+
end
1020+
if VERSION >= v"0.4.0-dev+6353"
1021+
f = f === :$ ? :(Base.XorFun()) :
1022+
f === :.+ ? :(Base.DotAddFun()) :
1023+
f === :.- ? :(Base.DotSubFun()) :
1024+
f === :.* ? :(Base.DotMulFun()) :
1025+
f === :mod ? :(Base.ModFun()) :
1026+
f === :rem ? :(Base.RemFun()) :
1027+
# DotRemFun is defined, but ::call(::DotRemFun, ...) is not until later
1028+
#f === :.% ? :(Base.DotRemFun()) :
1029+
f === :.<< ? :(Base.DotLSFun()) :
1030+
f === :.>> ? :(Base.DotRSFun()) :
1031+
f
1032+
end
1033+
if VERSION >= v"0.4.0-dev+6359"
1034+
f = f === :./ ? :(Base.DotRDivFun()) :
1035+
f
1036+
end
1037+
if VERSION >= v"0.4.0-rc1+59"
1038+
f = f === :max ? :(Base.ElementwiseMaxFun()) :
1039+
f === :min ? :(Base.ElementwiseMinFun()) :
1040+
f
1041+
end
1042+
if VERSION >= v"0.5.0-dev+741"
1043+
f = f === :complex ? :(Base.ComplexFun()) :
1044+
f === :dot ? :(Base.DotFun()) :
1045+
f
1046+
end
1047+
if VERSION >= v"0.5.0-dev+1472"
1048+
f = f === symbol("") ? :(Base.DotIDivFun()) :
1049+
f === :.% ? :(Base.DotRemFun()) :
1050+
f
1051+
end
1052+
f
1053+
end
1054+
end
1055+
9801056
end # module

test/runtests.jl

+66
Original file line numberDiff line numberDiff line change
@@ -994,3 +994,69 @@ cd(dirwalk) do
994994

995995
end
996996
rm(dirwalk, recursive=true)
997+
998+
# @functorize
999+
function checkfunc(Fun, func)
1000+
# TODO replace with proper condition on version once Julia#15804 is merged
1001+
if VERSION >= v"0.5.0-" && !isdefined(Base, :BitFunctorUnary)
1002+
@eval @test @functorize($(func)) === Base.$(func)
1003+
else
1004+
if isdefined(Base, Fun)
1005+
@eval @test isa(@functorize($(func)), Base.$(Fun))
1006+
else
1007+
@eval @test isa(@functorize($(func)), Function)
1008+
@eval @test @functorize($(func)) === Base.$(func)
1009+
end
1010+
end
1011+
end
1012+
1013+
for (Fun, func) in [(:IdFun, :identity),
1014+
(:AbsFun, :abs),
1015+
(:Abs2Fun, :abs2),
1016+
(:ExpFun, :exp),
1017+
(:LogFun, :log),
1018+
(:ConjFun, :conj),
1019+
(:ComplexFun, :complex)]
1020+
begin
1021+
if isdefined(Base, func)
1022+
checkfunc(Fun, func)
1023+
a = rand(1:10, 10)
1024+
@eval @test mapreduce($(func), +, $(a)) == mapreduce(@functorize($(func)), +, $(a))
1025+
end
1026+
end
1027+
end
1028+
for (Fun, func) in [(:AndFun, :&),
1029+
(:OrFun, :|),
1030+
(:XorFun, :$),
1031+
(:AddFun, :+),
1032+
(:DotAddFun, :.+),
1033+
(:SubFun, :-),
1034+
(:DotSubFun, :.-),
1035+
(:MulFun, :*),
1036+
(:DotMulFun, :.*),
1037+
(:RDivFun, :/),
1038+
(:DotRDivFun, :./),
1039+
(:LDivFun, :\),
1040+
(:IDivFun, :div),
1041+
(:DotIDivFun, symbol("")),
1042+
(:ModFun, :mod),
1043+
(:RemFun, :rem),
1044+
(:DotRemFun, :.%),
1045+
(:PowFun, :^),
1046+
(:MaxFun, :scalarmax),
1047+
(:MinFun, :scalarmin),
1048+
(:LessFun, :<),
1049+
(:MoreFun, :>),
1050+
(:DotLSFun, :.<<),
1051+
(:DotRSFun, :.>>),
1052+
(:ElementwiseMaxFun, :max),
1053+
(:ElementwiseMinFun, :min),
1054+
(:DotFun, :dot)]
1055+
begin
1056+
if isdefined(Base, func) && (func !== :.>> || VERSION >= v"0.4.0-dev+553") && (func !== :.% || VERSION >= v"0.5.0-dev+1472")
1057+
checkfunc(Fun, func)
1058+
a = rand(1:10, 10)
1059+
@eval @test mapreduce(identity, Base.$(func), $(a)) == mapreduce(identity, @functorize($(func)), $(a))
1060+
end
1061+
end
1062+
end

0 commit comments

Comments
 (0)