Skip to content

Commit 195de1a

Browse files
committed
Merge pull request #184 from martinholters/functorize
Add @functorize
2 parents cc3f69b + 2aacc82 commit 195de1a

File tree

3 files changed

+166
-0
lines changed

3 files changed

+166
-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

+77
Original file line numberDiff line numberDiff line change
@@ -980,4 +980,81 @@ if !isdefined(Base, :istextmime)
980980
istextmime(m::@compat(Union{MIME,AbstractString})) = istext(m)
981981
end
982982

983+
export @functorize
984+
macro functorize(f)
985+
if VERSION >= v"0.5.0-dev+3701"
986+
f === :scalarmax ? :(Base.scalarmax) :
987+
f === :scalarmin ? :(Base.scalarmin) :
988+
f === :centralizedabs2fun ? :(typeof(Base.centralizedabs2fun(0)).name.primary) :
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 === :centralizedabs2fun ? :(Base.CentralizedAbs2Fun) :
1003+
f
1004+
if VERSION >= v"0.4.0-dev+4902"
1005+
f = f === :< ? :(Base.LessFun()) :
1006+
f === :> ? :(Base.MoreFun()) :
1007+
f
1008+
end
1009+
if VERSION >= v"0.4.0-dev+4902"
1010+
f = f === :conj ? :(Base.ConjFun()) :
1011+
f
1012+
end
1013+
if VERSION >= v"0.4.0-dev+6254"
1014+
f = f === :- ? :(Base.SubFun()) :
1015+
f === :^ ? :(Base.PowFun()) :
1016+
f
1017+
end
1018+
if VERSION >= v"0.4.0-dev+6256"
1019+
f = f === :/ ? :(Base.RDivFun()) :
1020+
f === :\ ? :(Base.LDivFun()) :
1021+
f === :div ? :(Base.IDivFun()) :
1022+
f
1023+
end
1024+
if VERSION >= v"0.4.0-dev+6353"
1025+
f = f === :$ ? :(Base.XorFun()) :
1026+
f === :.+ ? :(Base.DotAddFun()) :
1027+
f === :.- ? :(Base.DotSubFun()) :
1028+
f === :.* ? :(Base.DotMulFun()) :
1029+
f === :mod ? :(Base.ModFun()) :
1030+
f === :rem ? :(Base.RemFun()) :
1031+
# DotRemFun is defined, but ::call(::DotRemFun, ...) is not until later
1032+
#f === :.% ? :(Base.DotRemFun()) :
1033+
f === :.<< ? :(Base.DotLSFun()) :
1034+
f === :.>> ? :(Base.DotRSFun()) :
1035+
f
1036+
end
1037+
if VERSION >= v"0.4.0-dev+6359"
1038+
f = f === :./ ? :(Base.DotRDivFun()) :
1039+
f
1040+
end
1041+
if VERSION >= v"0.4.0-rc1+59"
1042+
f = f === :max ? :(Base.ElementwiseMaxFun()) :
1043+
f === :min ? :(Base.ElementwiseMinFun()) :
1044+
f
1045+
end
1046+
if VERSION >= v"0.5.0-dev+741"
1047+
f = f === :complex ? :(Base.SparseArrays.ComplexFun()) :
1048+
f === :dot ? :(Base.SparseArrays.DotFun()) :
1049+
f
1050+
end
1051+
if VERSION >= v"0.5.0-dev+1472"
1052+
f = f === symbol("") ? :(Base.DotIDivFun()) :
1053+
f === :.% ? :(Base.DotRemFun()) :
1054+
f
1055+
end
1056+
f
1057+
end
1058+
end
1059+
9831060
end # module

test/runtests.jl

+87
Original file line numberDiff line numberDiff line change
@@ -1004,3 +1004,90 @@ 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+
if VERSION >= v"0.5.0-dev+3701"
1011+
@eval @test @functorize($(func)) === Base.$(func)
1012+
else
1013+
if isdefined(Base, Fun)
1014+
@eval @test isa(@functorize($(func)), Base.$(Fun))
1015+
else
1016+
@eval @test isa(@functorize($(func)), Function)
1017+
@eval @test @functorize($(func)) === Base.$(func)
1018+
end
1019+
end
1020+
end
1021+
1022+
for (Fun, func) in [(:IdFun, :identity),
1023+
(:AbsFun, :abs),
1024+
(:Abs2Fun, :abs2),
1025+
(:ExpFun, :exp),
1026+
(:LogFun, :log),
1027+
(:ConjFun, :conj)]
1028+
begin
1029+
if isdefined(Base, func)
1030+
checkfunc(Fun, func)
1031+
a = rand(1:10, 10)
1032+
@eval @test mapreduce($(func), +, $(a)) == mapreduce(@functorize($(func)), +, $(a))
1033+
end
1034+
end
1035+
end
1036+
1037+
for (Fun, func) in [(:AndFun, :&),
1038+
(:OrFun, :|),
1039+
(:XorFun, :$),
1040+
(:AddFun, :+),
1041+
(:DotAddFun, :.+),
1042+
(:SubFun, :-),
1043+
(:DotSubFun, :.-),
1044+
(:MulFun, :*),
1045+
(:DotMulFun, :.*),
1046+
(:RDivFun, :/),
1047+
(:DotRDivFun, :./),
1048+
(:LDivFun, :\),
1049+
(:IDivFun, :div),
1050+
(:DotIDivFun, symbol("")),
1051+
(:ModFun, :mod),
1052+
(:RemFun, :rem),
1053+
(:DotRemFun, :.%),
1054+
(:PowFun, :^),
1055+
(:MaxFun, :scalarmax),
1056+
(:MinFun, :scalarmin),
1057+
(:LessFun, :<),
1058+
(:MoreFun, :>),
1059+
(:DotLSFun, :.<<),
1060+
(:DotRSFun, :.>>),
1061+
(:ElementwiseMaxFun, :max),
1062+
(:ElementwiseMinFun, :min)]
1063+
begin
1064+
if isdefined(Base, func) && (func !== :.>> || VERSION >= v"0.4.0-dev+553") && (func !== :.% || VERSION >= v"0.5.0-dev+1472")
1065+
checkfunc(Fun, func)
1066+
a = rand(1:10, 10)
1067+
@eval @test mapreduce(identity, Base.$(func), $(a)) == mapreduce(identity, @functorize($(func)), $(a))
1068+
end
1069+
end
1070+
end
1071+
1072+
if VERSION >= v"0.5.0-dev+3701"
1073+
@test @functorize(complex) === complex
1074+
@test @functorize(dot) === dot
1075+
else
1076+
if isdefined(Base, :SparseArrays) && isdefined(Base.SparseArrays, :ComplexFun)
1077+
@test isa(@functorize(complex), Base.SparseArrays.ComplexFun)
1078+
@test isa(@functorize(dot), Base.SparseArrays.DotFun)
1079+
else
1080+
@test isa(@functorize(complex), Function)
1081+
@test isa(@functorize(dot), Function)
1082+
@test @functorize(complex) === complex
1083+
@test @functorize(dot) === dot
1084+
end
1085+
end
1086+
let a = rand(1:10, 10)
1087+
@test mapreduce(identity, dot, a) == mapreduce(identity, @functorize(dot), a)
1088+
end
1089+
@test isa(@functorize(centralizedabs2fun)(1), @functorize(centralizedabs2fun))
1090+
@test isa(@functorize(centralizedabs2fun)(1.0), @functorize(centralizedabs2fun))
1091+
let a = rand(1:10, 10)
1092+
@eval @test mapreduce(x -> abs2(x - 1), +, $(a)) == mapreduce(@functorize(centralizedabs2fun)(1), +, $(a))
1093+
end

0 commit comments

Comments
 (0)