diff --git a/base/exports.jl b/base/exports.jl index 1ab3c1602d301..70b0de2e2610f 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -256,6 +256,7 @@ export ~, :, =>, + ∘, A_ldiv_B!, A_ldiv_Bc, A_ldiv_Bt, diff --git a/base/operators.jl b/base/operators.jl index 6570a14e3e612..117897299d0cd 100644 --- a/base/operators.jl +++ b/base/operators.jl @@ -328,9 +328,61 @@ eltype(::Type{Any}) = Any eltype(t::DataType) = eltype(supertype(t)) eltype(x) = eltype(typeof(x)) -# function pipelining +# function pipelining, composition & negation + |>(x, f) = f(x) +""" + ∘(f, g) + +Creates a composition of two functions (or callable objects) `f` and `g`, such +that `(f ∘ g)(x...) == f(g(x...))`. The `∘` symbol can be accessed at the REPL +using `\\circ`. + +By default, a function equivalent to `(x...) -> f(g(x...))` is returned, but +this may be specialized to create any functionally-equivalent, callable object. +""" +function ∘(f, g) + # Avoids splatting penalty. TODO: remove when that is fixed. + # Chose to implement up to length 16 (current setting of MAX_TUPLE_SPLAT) + composed() = f(g()) + composed(x1) = f(g(x1)) + composed(x1,x2) = f(g(x1,x2)) + composed(x1,x2,x3) = f(g(x1,x2,x3)) + composed(x1,x2,x3,x4) = f(g(x1,x2,x3,x4)) + composed(x1,x2,x3,x4,x5) = f(g(x1,x2,x3,x4,x5)) + composed(x1,x2,x3,x4,x5,x6) = f(g(x1,x2,x3,x4,x5,x6)) + composed(x1,x2,x3,x4,x5,x6,x7) = f(g(x1,x2,x3,x4,x5,x6,x7)) + composed(x1,x2,x3,x4,x5,x6,x7,x8) = f(g(x1,x2,x3,x4,x5,x6,x7,x8)) + composed(x1,x2,x3,x4,x5,x6,x7,x8,x9) = f(g(x1,x2,x3,x4,x5,x6,x7,x8,x9)) + composed(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10) = + f(g(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10)) + composed(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11) = + f(g(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11)) + composed(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12) = + f(g(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12)) + composed(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13) = + f(g(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13)) + composed(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14) = + f(g(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14)) + composed(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15) = + f(g(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15)) + composed(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16) = + f(g(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16)) + + composed(xs...) = f(g(xs...)) + + return composed # This name is remembered and visible at the REPL +end + +""" + !(f::Function) + +Returns a new function which applies boolean not to the output of `f`, +equivalent to `(x...) -> !f(x...)`. +""" +!(f::Function) = (!) ∘ f + # array shape rules function promote_shape(a::Tuple{Int,}, b::Tuple{Int,}) @@ -672,6 +724,7 @@ export ∪, √, ∛, + ∘, colon, hcat, vcat, @@ -685,6 +738,6 @@ import ..this_module: !, !=, $, %, .%, ÷, .÷, &, *, +, -, .!=, .+, .-, .*, ./, .>=, .\, .^, /, //, <, <:, <<, <=, ==, >, >=, >>, .>>, .<<, >>>, <|, |>, \, ^, |, ~, !==, ===, >:, colon, hcat, vcat, hvcat, getindex, setindex!, transpose, ctranspose, - ≥, ≤, ≠, .≥, .≤, .≠, ⋅, ×, ∈, ∉, ∋, ∌, ⊆, ⊈, ⊊, ∩, ∪, √, ∛ + ≥, ≤, ≠, .≥, .≤, .≠, ⋅, ×, ∈, ∉, ∋, ∌, ⊆, ⊈, ⊊, ∩, ∪, √, ∛, ∘ end diff --git a/doc/stdlib/base.rst b/doc/stdlib/base.rst index 7e3e92c70930f..d3b6640727b02 100644 --- a/doc/stdlib/base.rst +++ b/doc/stdlib/base.rst @@ -668,6 +668,20 @@ Generic Functions julia> [1:5;] |> x->x.^2 |> sum |> inv 0.01818181818181818 +.. function:: ∘(f, g) + + .. Docstring generated from Julia source + + Creates a composition of two functions (or functor objects), such that ``(f ∘ g)(x...) == f(g(x...))``\ . The ``∘`` symbol can be accessed at the REPL using ``\circ``\ . + + By default, a function equivalent to ``(x...) -> f(g(x...))`` is returned, but this may be specialized to create any functionally-equivalent, callable object. + +.. function:: !(f::Function) + + .. Docstring generated from Julia source + + Returns a new function which applies boolean not to the output of ``f``\ , equivalent to ``(x...) -> !f(x...)``\ . + Syntax ------ diff --git a/test/operators.jl b/test/operators.jl index 36f80a6033ddf..e8a66e893a4fd 100644 --- a/test/operators.jl +++ b/test/operators.jl @@ -55,3 +55,6 @@ let xs = [[i:i+4;] for i in 1:10] @test max(xs[1:n]...) == [n:n+4;] end end + +@test ((x -> x+1) ∘ (x -> 2x))(5) == 11 +@test (!isless)(1,2) == false