Skip to content

Commit

Permalink
add one arg function composition (#34251)
Browse files Browse the repository at this point in the history
  • Loading branch information
jw3126 authored and KristofferC committed Apr 11, 2020
1 parent deeaedb commit b22ee57
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 0 deletions.
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ New library functions

New library features
--------------------
* Function composition now works also on one argument `∘(f) = f` (#34251)


Standard library changes
Expand Down
5 changes: 5 additions & 0 deletions base/operators.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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 one function ∘(f) requires at least Julia 1.5.
# Examples
```jldoctest
julia> map(uppercase∘first, ["apple", "banana", "carrot"])
Expand All @@ -856,6 +859,8 @@ julia> ∘(fs...)(3)
3.0
```
"""
function end
(f) = f
(f, g) = (x...)->f(g(x...))
(f, g, h...) = (f g, h...)

Expand Down
23 changes: 23 additions & 0 deletions test/operators.jl
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,30 @@ 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"

# 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)
@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]" => "")
Expand Down

0 comments on commit b22ee57

Please sign in to comment.