From 85a4924caddf73ab220fe97f9c9e2597015df8f4 Mon Sep 17 00:00:00 2001 From: Jan Weidner Date: Fri, 3 Jan 2020 21:32:24 +0100 Subject: [PATCH 1/7] add zero and one arg function composition --- base/operators.jl | 2 ++ test/operators.jl | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/base/operators.jl b/base/operators.jl index 049fdf3410273..b88dba0624461 100644 --- a/base/operators.jl +++ b/base/operators.jl @@ -856,6 +856,8 @@ julia> ∘(fs...)(3) 3.0 ``` """ +∘() = identity +∘(f) = f ∘(f, g) = (x...)->f(g(x...)) ∘(f, g, h...) = ∘(f ∘ g, h...) diff --git a/test/operators.jl b/test/operators.jl index 58102d7d65a32..84f6463e52938 100644 --- a/test/operators.jl +++ b/test/operators.jl @@ -111,7 +111,25 @@ Base.promote_rule(::Type{T19714}, ::Type{Int}) = T19714 @test ∘(x -> x-2, x -> x-3, x -> x+5)(7) == 7 fs = [x -> x[1:2], uppercase, lowercase] @test ∘(fs...)("ABC") == "AB" + + @test ∘()(0) === 0 + @test ∘(x -> (x, 1))(0) === (0, 1) + @test ∘(x -> (x, 2), x -> (x, 1))(0) === ((0, 1), 2) + @test ∘(x -> (x, 3), x -> (x, 2), x->(x,1))(0) === (((0, 1), 2), 3) + @test ∘(x -> (x, 4), x -> (x, 3), x->(x,2), x-> (x, 1))(0) === ((((0, 1), 2), 3), 4) + + # test that user defined functors only need to overload the two arg version + struct FreeMagma + word + end + Base.:(∘)(a::FreeMagma, b::FreeMagma) = FreeMagma((a.word, b.word)) + + @test ∘(FreeMagma(1)) === FreeMagma(1) + @test ∘(FreeMagma(1), FreeMagma(2)) === FreeMagma((1,2)) + @test ∘(FreeMagma(1), FreeMagma(2), FreeMagma(3)) === FreeMagma(((1,2), 3)) + @test ∘(FreeMagma(1), FreeMagma(2), FreeMagma(3), FreeMagma(4)) === FreeMagma((((1,2), 3), 4)) end + @testset "function negation" begin str = randstring(20) @test filter(!isuppercase, str) == replace(str, r"[A-Z]" => "") From 326b1fe8755071fdfe9ee7de42a1267b6ef0fd4a Mon Sep 17 00:00:00 2001 From: Jan Weidner Date: Sat, 4 Jan 2020 08:05:10 +0100 Subject: [PATCH 2/7] add function composition news and compat --- NEWS.md | 1 + base/operators.jl | 3 +++ 2 files changed, 4 insertions(+) diff --git a/NEWS.md b/NEWS.md index 139c9238de7f1..e65b9417239aa 100644 --- a/NEWS.md +++ b/NEWS.md @@ -23,6 +23,7 @@ New library functions New library features -------------------- +* Function composition now works also on zero or one arguments `∘()=identity; ∘(f) = f` (#34251) Standard library changes diff --git a/base/operators.jl b/base/operators.jl index b88dba0624461..546e9dc93d160 100644 --- a/base/operators.jl +++ b/base/operators.jl @@ -837,6 +837,9 @@ and splatting `∘(fs...)` for composing an iterable collection of functions. !!! compat "Julia 1.4" Multiple function composition requires at least Julia 1.4. +!!! compat "Julia 1.5" + Composition of zero or one functions requires at least Julia 1.5. + # Examples ```jldoctest julia> map(uppercase∘first, ["apple", "banana", "carrot"]) From 5289aab41f66ec22414681b1f6f180625a67d0aa Mon Sep 17 00:00:00 2001 From: Jan Weidner Date: Thu, 9 Jan 2020 20:53:02 +0100 Subject: [PATCH 3/7] remove empty function composition --- base/operators.jl | 12 ++++++++++-- test/operators.jl | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/base/operators.jl b/base/operators.jl index 546e9dc93d160..e5052650c4593 100644 --- a/base/operators.jl +++ b/base/operators.jl @@ -838,7 +838,8 @@ and splatting `∘(fs...)` for composing an iterable collection of functions. Multiple function composition requires at least Julia 1.4. !!! compat "Julia 1.5" - Composition of zero or one functions requires at least Julia 1.5. + Composition of one function ∘(f) requires at least Julia 1.5. + # Examples ```jldoctest @@ -859,7 +860,14 @@ julia> ∘(fs...)(3) 3.0 ``` """ -∘() = identity +function ∘() + # Like +() and *() we leave ∘() undefined. + # While `∘() = identity` is a reasonable definition for functions, this + # would cause headaches for composition of user defined morphisms. + # See also #34251 + msg = """Empty composition ∘() is undefined.""" + throw(ArgumentError(msg)) +end ∘(f) = f ∘(f, g) = (x...)->f(g(x...)) ∘(f, g, h...) = ∘(f ∘ g, h...) diff --git a/test/operators.jl b/test/operators.jl index 84f6463e52938..b667085f6900a 100644 --- a/test/operators.jl +++ b/test/operators.jl @@ -112,7 +112,7 @@ Base.promote_rule(::Type{T19714}, ::Type{Int}) = T19714 fs = [x -> x[1:2], uppercase, lowercase] @test ∘(fs...)("ABC") == "AB" - @test ∘()(0) === 0 + @test_throws ArgumentError ∘() @test ∘(x -> (x, 1))(0) === (0, 1) @test ∘(x -> (x, 2), x -> (x, 1))(0) === ((0, 1), 2) @test ∘(x -> (x, 3), x -> (x, 2), x->(x,1))(0) === (((0, 1), 2), 3) From 6458eb9490a5a468748e0e7005f6477300cd94e2 Mon Sep 17 00:00:00 2001 From: Jan Weidner Date: Thu, 9 Jan 2020 20:53:38 +0100 Subject: [PATCH 4/7] fix --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index e65b9417239aa..a28b48b0e35be 100644 --- a/NEWS.md +++ b/NEWS.md @@ -23,7 +23,7 @@ New library functions New library features -------------------- -* Function composition now works also on zero or one arguments `∘()=identity; ∘(f) = f` (#34251) +* Function composition now works also on one argument `∘(f) = f` (#34251) Standard library changes From ae03f24c656d73b795074600e7a056df75c0feae Mon Sep 17 00:00:00 2001 From: Jan Weidner Date: Thu, 9 Jan 2020 23:18:06 +0100 Subject: [PATCH 5/7] fix --- base/operators.jl | 9 +-------- test/operators.jl | 7 ++++++- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/base/operators.jl b/base/operators.jl index e5052650c4593..6479da458987d 100644 --- a/base/operators.jl +++ b/base/operators.jl @@ -860,14 +860,7 @@ julia> ∘(fs...)(3) 3.0 ``` """ -function ∘() - # Like +() and *() we leave ∘() undefined. - # While `∘() = identity` is a reasonable definition for functions, this - # would cause headaches for composition of user defined morphisms. - # See also #34251 - msg = """Empty composition ∘() is undefined.""" - throw(ArgumentError(msg)) -end +function ∘ end ∘(f) = f ∘(f, g) = (x...)->f(g(x...)) ∘(f, g, h...) = ∘(f ∘ g, h...) diff --git a/test/operators.jl b/test/operators.jl index b667085f6900a..b6143d1157c12 100644 --- a/test/operators.jl +++ b/test/operators.jl @@ -112,7 +112,12 @@ Base.promote_rule(::Type{T19714}, ::Type{Int}) = T19714 fs = [x -> x[1:2], uppercase, lowercase] @test ∘(fs...)("ABC") == "AB" - @test_throws ArgumentError ∘() + # Like +() and *() we leave ∘() undefined. + # While `∘() = identity` is a reasonable definition for functions, this + # would cause headaches for composition of user defined morphisms. + # See also #34251 + @test_throws MethodError ∘() + @test ∘(x -> (x, 1))(0) === (0, 1) @test ∘(x -> (x, 2), x -> (x, 1))(0) === ((0, 1), 2) @test ∘(x -> (x, 3), x -> (x, 2), x->(x,1))(0) === (((0, 1), 2), 3) From 1630d3271a84dbbb7d533b634bfdd7f7027483f5 Mon Sep 17 00:00:00 2001 From: Jan Weidner Date: Fri, 10 Jan 2020 21:16:15 +0100 Subject: [PATCH 6/7] fix --- test/operators.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/operators.jl b/test/operators.jl index b6143d1157c12..64c0ebab5ab37 100644 --- a/test/operators.jl +++ b/test/operators.jl @@ -116,7 +116,7 @@ Base.promote_rule(::Type{T19714}, ::Type{Int}) = T19714 # While `∘() = identity` is a reasonable definition for functions, this # would cause headaches for composition of user defined morphisms. # See also #34251 - @test_throws MethodError ∘() + @test_throws(MethodError, ∘()) @test ∘(x -> (x, 1))(0) === (0, 1) @test ∘(x -> (x, 2), x -> (x, 1))(0) === ((0, 1), 2) From fffce8a06d14ffea50e9e844c907bff2327eb850 Mon Sep 17 00:00:00 2001 From: Jan Weidner Date: Sun, 12 Jan 2020 21:09:32 +0100 Subject: [PATCH 7/7] fix --- base/operators.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/base/operators.jl b/base/operators.jl index 6479da458987d..cd673f791e527 100644 --- a/base/operators.jl +++ b/base/operators.jl @@ -839,7 +839,6 @@ and splatting `∘(fs...)` for composing an iterable collection of functions. !!! compat "Julia 1.5" Composition of one function ∘(f) requires at least Julia 1.5. - # Examples ```jldoctest