diff --git a/NEWS.md b/NEWS.md index a47056e19127a..f7afd2de59434 100644 --- a/NEWS.md +++ b/NEWS.md @@ -8,6 +8,9 @@ New language features * `import` now allows quoted symbols, e.g. `import Base.:+` ([#33158]). +* Function composition now supports multiple functions: `∘(f, g, h) = f ∘ g ∘ h` +and splatting `∘(fs...)` for composing an iterable collection of functions ([#33568]). + Language changes ---------------- diff --git a/base/operators.jl b/base/operators.jl index 51105d21c2816..232f6407ed6f5 100644 --- a/base/operators.jl +++ b/base/operators.jl @@ -827,6 +827,13 @@ julia> [1:5;] |> x->x.^2 |> sum |> inv Compose functions: i.e. `(f ∘ g)(args...)` means `f(g(args...))`. The `∘` symbol can be entered in the Julia REPL (and most editors, appropriately configured) by typing `\\circ`. +Function composition also works in prefix form: `∘(f, g)` is the same as `f ∘ g`. +The prefix form supports composition of multiple functions: `∘(f, g, h) = f ∘ g ∘ h` +and splatting `∘(fs...)` for composing an iterable collection of functions. + +!!!compat "Julia 1.4" + Multiple function composition requires at least Julia 1.4. + # Examples ```jldoctest julia> map(uppercase∘first, ["apple", "banana", "carrot"]) @@ -834,10 +841,20 @@ julia> map(uppercase∘first, ["apple", "banana", "carrot"]) 'A' 'B' 'C' + +julia> fs = [ + x -> 2x + x -> x/2 + x -> x-1 + x -> x+1 + ]; + +julia> ∘(fs...)(3) +3.0 ``` """ ∘(f, g) = (x...)->f(g(x...)) - +∘(f, g, h...) = ∘(f ∘ g, h...) """ !f::Function diff --git a/test/operators.jl b/test/operators.jl index 467d3aa755e2c..58102d7d65a32 100644 --- a/test/operators.jl +++ b/test/operators.jl @@ -105,9 +105,12 @@ Base.convert(::Type{T19714}, ::Int) = T19714() Base.promote_rule(::Type{T19714}, ::Type{Int}) = T19714 @test T19714()/1 === 1/T19714() === T19714() -# pr #17155 +# pr #17155 and #33568 @testset "function composition" begin @test (uppercase∘(x->string(x,base=16)))(239487) == "3A77F" + @test ∘(x -> x-2, x -> x-3, x -> x+5)(7) == 7 + fs = [x -> x[1:2], uppercase, lowercase] + @test ∘(fs...)("ABC") == "AB" end @testset "function negation" begin str = randstring(20)