From dc8e1b545b74adfa1aa95d76c233e21b0f8adf7f Mon Sep 17 00:00:00 2001 From: Stefan Karpinski Date: Mon, 27 Jun 2016 15:42:08 -0400 Subject: [PATCH 1/7] =?UTF-8?q?function=20negation=20and=20composition:=20?= =?UTF-8?q?!f,=20f=E2=88=98g?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- base/exports.jl | 1 + base/operators.jl | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) 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..c572026fcba76 100644 --- a/base/operators.jl +++ b/base/operators.jl @@ -328,9 +328,14 @@ 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) = (x...)->f(g(x...)) + +!(f::Function) = (x...)->!f(x...) + # array shape rules function promote_shape(a::Tuple{Int,}, b::Tuple{Int,}) @@ -672,6 +677,7 @@ export ∪, √, ∛, + ∘, colon, hcat, vcat, @@ -685,6 +691,6 @@ import ..this_module: !, !=, $, %, .%, ÷, .÷, &, *, +, -, .!=, .+, .-, .*, ./, .>=, .\, .^, /, //, <, <:, <<, <=, ==, >, >=, >>, .>>, .<<, >>>, <|, |>, \, ^, |, ~, !==, ===, >:, colon, hcat, vcat, hvcat, getindex, setindex!, transpose, ctranspose, - ≥, ≤, ≠, .≥, .≤, .≠, ⋅, ×, ∈, ∉, ∋, ∌, ⊆, ⊈, ⊊, ∩, ∪, √, ∛ + ≥, ≤, ≠, .≥, .≤, .≠, ⋅, ×, ∈, ∉, ∋, ∌, ⊆, ⊈, ⊊, ∩, ∪, √, ∛, ∘ end From bda90527262aefd2a549e12055863b20e6ac250a Mon Sep 17 00:00:00 2001 From: Andy Ferris Date: Wed, 29 Jun 2016 13:52:35 +1000 Subject: [PATCH 2/7] Added docstrings and tests for compositions --- base/operators.jl | 17 +++++++++++++++++ test/operators.jl | 3 +++ 2 files changed, 20 insertions(+) diff --git a/base/operators.jl b/base/operators.jl index c572026fcba76..9831ea71e14aa 100644 --- a/base/operators.jl +++ b/base/operators.jl @@ -332,8 +332,25 @@ eltype(x) = eltype(typeof(x)) |>(x, f) = f(x) +""" + ∘(f, g) + f ∘ g + +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, an anonymous function `(x...) -> f(g(x...))` is returned, but this may +be specialized to create any functionally-equivalent callable object. +""" ∘(f, g) = (x...)->f(g(x...)) +""" + !(f::Function) + +Returns a new function which applies boolean not to the output of `f`, equal to +`(x...) -> !f(x...)`. +""" !(f::Function) = (x...)->!f(x...) # array shape rules diff --git a/test/operators.jl b/test/operators.jl index 36f80a6033ddf..c3d63b3e25b05 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 From 786c579155d14b13f082fec3fc7f1b22d9bf0c44 Mon Sep 17 00:00:00 2001 From: Andy Ferris Date: Wed, 29 Jun 2016 15:10:29 +1000 Subject: [PATCH 3/7] rst for docs --- base/operators.jl | 3 +-- doc/stdlib/base.rst | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/base/operators.jl b/base/operators.jl index 9831ea71e14aa..971b1830c5cf2 100644 --- a/base/operators.jl +++ b/base/operators.jl @@ -334,7 +334,6 @@ eltype(x) = eltype(typeof(x)) """ ∘(f, g) - f ∘ g 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 @@ -349,7 +348,7 @@ be specialized to create any functionally-equivalent callable object. !(f::Function) Returns a new function which applies boolean not to the output of `f`, equal to -`(x...) -> !f(x...)`. +`(x...) -> !f(x...)`. """ !(f::Function) = (x...)->!f(x...) diff --git a/doc/stdlib/base.rst b/doc/stdlib/base.rst index 7e3e92c70930f..b8dbf16496b8b 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, an anonymous function ``(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``\ , equal to ``(x...) -> !f(x...)``\ . + Syntax ------ From 21fcc9188566f1b89b1d31bab136557e6015f21c Mon Sep 17 00:00:00 2001 From: Andy Ferris Date: Wed, 29 Jun 2016 17:16:15 +1000 Subject: [PATCH 4/7] Changed to closure approach: no splatting penalty Decided to make up to 16 inputs fast, to match MAX_TUPLE_SPLAT. Conveniently, closures also allow for introspection of f and g. --- base/operators.jl | 74 +++++++++++++++++++++++++++++++++++++++++---- doc/stdlib/base.rst | 4 +-- 2 files changed, 70 insertions(+), 8 deletions(-) diff --git a/base/operators.jl b/base/operators.jl index 971b1830c5cf2..8868e2872793f 100644 --- a/base/operators.jl +++ b/base/operators.jl @@ -339,18 +339,80 @@ 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, an anonymous function `(x...) -> f(g(x...))` is returned, but this may -be specialized to create any functionally-equivalent callable object. +By default, a function equivalent to `(x...) -> f(g(x...))` is returned, but +this may be specialized to create any functionally-equivalent, callable object. """ -∘(f, g) = (x...)->f(g(x...)) +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`, equal to -`(x...) -> !f(x...)`. +Returns a new function which applies boolean not to the output of `f`, +equivalent to `(x...) -> !f(x...)`. """ -!(f::Function) = (x...)->!f(x...) +function !(f::Function) + # Avoids splatting penalty. TODO: remove when that is fixed. + # Chose to implement up to length 16 (current setting of MAX_TUPLE_SPLAT) + negated() = !(f()) + negated(x1) = !(f(x1)) + negated(x1,x2) = !(f(x1,x2)) + negated(x1,x2,x3) = !(f(x1,x2,x3)) + negated(x1,x2,x3,x4) = !(f(x1,x2,x3,x4)) + negated(x1,x2,x3,x4,x5) = !(f(x1,x2,x3,x4,x5)) + negated(x1,x2,x3,x4,x5,x6) = !(f(x1,x2,x3,x4,x5,x6)) + negated(x1,x2,x3,x4,x5,x6,x7) = !(f(x1,x2,x3,x4,x5,x6,x7)) + negated(x1,x2,x3,x4,x5,x6,x7,x8) = !(f(x1,x2,x3,x4,x5,x6,x7,x8)) + negated(x1,x2,x3,x4,x5,x6,x7,x8,x9) = !(f(x1,x2,x3,x4,x5,x6,x7,x8,x9)) + negated(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10) = + !(f(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10)) + negated(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11) = + !(f(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11)) + negated(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12) = + !(f(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12)) + negated(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13) = + !(f(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13)) + negated(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14) = + !(f(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14)) + negated(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15) = + !(f(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15)) + negated(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16) = + !(f(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16)) + + negated(xs...) = !(f(xs...)) + + return negated # This name is remembered and visible at the REPL +end # array shape rules diff --git a/doc/stdlib/base.rst b/doc/stdlib/base.rst index b8dbf16496b8b..d3b6640727b02 100644 --- a/doc/stdlib/base.rst +++ b/doc/stdlib/base.rst @@ -674,13 +674,13 @@ Generic Functions 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, an anonymous function ``(x...) -> f(g(x...))`` is returned, but this may be specialized to create any functionally-equivalent callable object. + 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``\ , equal to ``(x...) -> !f(x...)``\ . + Returns a new function which applies boolean not to the output of ``f``\ , equivalent to ``(x...) -> !f(x...)``\ . Syntax ------ From e6a69b79a1ac0d07a65edaa79bca506a3c11de19 Mon Sep 17 00:00:00 2001 From: Andy Ferris Date: Thu, 30 Jun 2016 13:27:03 +1000 Subject: [PATCH 5/7] Simplified definition of function negation --- base/operators.jl | 33 +-------------------------------- 1 file changed, 1 insertion(+), 32 deletions(-) diff --git a/base/operators.jl b/base/operators.jl index 8868e2872793f..8d1133fac3a81 100644 --- a/base/operators.jl +++ b/base/operators.jl @@ -381,38 +381,7 @@ end Returns a new function which applies boolean not to the output of `f`, equivalent to `(x...) -> !f(x...)`. """ -function !(f::Function) - # Avoids splatting penalty. TODO: remove when that is fixed. - # Chose to implement up to length 16 (current setting of MAX_TUPLE_SPLAT) - negated() = !(f()) - negated(x1) = !(f(x1)) - negated(x1,x2) = !(f(x1,x2)) - negated(x1,x2,x3) = !(f(x1,x2,x3)) - negated(x1,x2,x3,x4) = !(f(x1,x2,x3,x4)) - negated(x1,x2,x3,x4,x5) = !(f(x1,x2,x3,x4,x5)) - negated(x1,x2,x3,x4,x5,x6) = !(f(x1,x2,x3,x4,x5,x6)) - negated(x1,x2,x3,x4,x5,x6,x7) = !(f(x1,x2,x3,x4,x5,x6,x7)) - negated(x1,x2,x3,x4,x5,x6,x7,x8) = !(f(x1,x2,x3,x4,x5,x6,x7,x8)) - negated(x1,x2,x3,x4,x5,x6,x7,x8,x9) = !(f(x1,x2,x3,x4,x5,x6,x7,x8,x9)) - negated(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10) = - !(f(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10)) - negated(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11) = - !(f(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11)) - negated(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12) = - !(f(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12)) - negated(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13) = - !(f(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13)) - negated(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14) = - !(f(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14)) - negated(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15) = - !(f(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15)) - negated(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16) = - !(f(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16)) - - negated(xs...) = !(f(xs...)) - - return negated # This name is remembered and visible at the REPL -end +!(f::Function) = (!) ∘ f # array shape rules From e3d9579315c634bd95e685548821122b11faf7ca Mon Sep 17 00:00:00 2001 From: Andy Ferris Date: Thu, 30 Jun 2016 13:27:03 +1000 Subject: [PATCH 6/7] Fixed typo in test --- test/operators.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/operators.jl b/test/operators.jl index c3d63b3e25b05..e8a66e893a4fd 100644 --- a/test/operators.jl +++ b/test/operators.jl @@ -56,5 +56,5 @@ let xs = [[i:i+4;] for i in 1:10] end end -@test ((x -> x+1) ∘ (x _> 2x))(5) == 11 +@test ((x -> x+1) ∘ (x -> 2x))(5) == 11 @test (!isless)(1,2) == false From b95404a02099817382f67a3405ba78c5db1b5788 Mon Sep 17 00:00:00 2001 From: Andy Ferris Date: Fri, 8 Jul 2016 07:14:21 +1000 Subject: [PATCH 7/7] Clarify docstring --- base/operators.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/base/operators.jl b/base/operators.jl index 8d1133fac3a81..117897299d0cd 100644 --- a/base/operators.jl +++ b/base/operators.jl @@ -335,9 +335,9 @@ eltype(x) = eltype(typeof(x)) """ ∘(f, g) -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`. +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.