Skip to content

Commit f98208e

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 cc3f69b commit f98208e

File tree

3 files changed

+162
-0
lines changed

3 files changed

+162
-0
lines changed

README.md

Lines changed: 2 additions & 0 deletions
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

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -980,4 +980,80 @@ if !isdefined(Base, :istextmime)
980980
istextmime(m::@compat(Union{MIME,AbstractString})) = istext(m)
981981
end
982982

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

test/runtests.jl

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1004,3 +1004,87 @@ if VERSION < v"0.4.0"
10041004
else
10051005
@compat rc = RemoteChannel{Channel{Any}}(1,2,3)
10061006
end
1007+
1008+
# @functorize
1009+
function checkfunc(Fun, func)
1010+
# TODO replace with proper condition on version once Julia#15804 is merged
1011+
if VERSION >= v"0.5.0-" && !isdefined(Base, :BitFunctorUnary)
1012+
@eval @test @functorize($(func)) === Base.$(func)
1013+
else
1014+
if isdefined(Base, Fun)
1015+
@eval @test isa(@functorize($(func)), Base.$(Fun))
1016+
else
1017+
@eval @test isa(@functorize($(func)), Function)
1018+
@eval @test @functorize($(func)) === Base.$(func)
1019+
end
1020+
end
1021+
end
1022+
1023+
for (Fun, func) in [(:IdFun, :identity),
1024+
(:AbsFun, :abs),
1025+
(:Abs2Fun, :abs2),
1026+
(:ExpFun, :exp),
1027+
(:LogFun, :log),
1028+
(:ConjFun, :conj)]
1029+
begin
1030+
if isdefined(Base, func)
1031+
checkfunc(Fun, func)
1032+
a = rand(1:10, 10)
1033+
@eval @test mapreduce($(func), +, $(a)) == mapreduce(@functorize($(func)), +, $(a))
1034+
end
1035+
end
1036+
end
1037+
1038+
for (Fun, func) in [(:AndFun, :&),
1039+
(:OrFun, :|),
1040+
(:XorFun, :$),
1041+
(:AddFun, :+),
1042+
(:DotAddFun, :.+),
1043+
(:SubFun, :-),
1044+
(:DotSubFun, :.-),
1045+
(:MulFun, :*),
1046+
(:DotMulFun, :.*),
1047+
(:RDivFun, :/),
1048+
(:DotRDivFun, :./),
1049+
(:LDivFun, :\),
1050+
(:IDivFun, :div),
1051+
(:DotIDivFun, symbol("")),
1052+
(:ModFun, :mod),
1053+
(:RemFun, :rem),
1054+
(:DotRemFun, :.%),
1055+
(:PowFun, :^),
1056+
(:MaxFun, :scalarmax),
1057+
(:MinFun, :scalarmin),
1058+
(:LessFun, :<),
1059+
(:MoreFun, :>),
1060+
(:DotLSFun, :.<<),
1061+
(:DotRSFun, :.>>),
1062+
(:ElementwiseMaxFun, :max),
1063+
(:ElementwiseMinFun, :min)]
1064+
begin
1065+
if isdefined(Base, func) && (func !== :.>> || VERSION >= v"0.4.0-dev+553") && (func !== :.% || VERSION >= v"0.5.0-dev+1472")
1066+
checkfunc(Fun, func)
1067+
a = rand(1:10, 10)
1068+
@eval @test mapreduce(identity, Base.$(func), $(a)) == mapreduce(identity, @functorize($(func)), $(a))
1069+
end
1070+
end
1071+
end
1072+
1073+
# TODO replace with proper condition on version once Julia#15804 is merged
1074+
if VERSION >= v"0.5.0-" && !isdefined(Base, :BitFunctorUnary)
1075+
@test @functorize(complex) === complex
1076+
@test @functorize(dot) === dot
1077+
else
1078+
if isdefined(Base, :SparseArrays) && isdefined(Base.SparseArrays, :ComplexFun)
1079+
@test isa(@functorize(complex), Base.SparseArrays.ComplexFun)
1080+
@test isa(@functorize(dot), Base.SparseArrays.DotFun)
1081+
else
1082+
@test isa(@functorize(complex), Function)
1083+
@test isa(@functorize(dot), Function)
1084+
@test @functorize(complex) === complex
1085+
@test @functorize(dot) === dot
1086+
end
1087+
end
1088+
let a = rand(1:10, 10)
1089+
@test mapreduce(identity, dot, a) == mapreduce(identity, @functorize(dot), a)
1090+
end

0 commit comments

Comments
 (0)