Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose ComposedFunction as a public API #37517

Merged
merged 15 commits into from
Sep 24, 2020
Merged
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ Standard library changes
* `RegexMatch` objects can now be probed for whether a named capture group exists within it through `haskey()` ([#36717]).
* For consistency `haskey(r::RegexMatch, i::Integer)` has also been added and returns if the capture group for `i` exists ([#37300]).
* A new standard library `TOML` has been added for parsing and printing [TOML files](https://toml.io) ([#37034]).
* The composition operator `∘` now returns a `Base.ComposedFunction` instead of an anonymous function ([#37517]).
* A new standard library `Downloads` has been added, which replaces the old `Base.download` function with `Downloads.download`, providing cross-platform, multi-protocol, in-process download functionality implemented with [libcurl](https://curl.haxx.se/libcurl/) ([#37340]).
* The `Pkg.BinaryPlatforms` module has been moved into `Base` as `Base.BinaryPlatforms` and heavily reworked.
Applications that want to be compatible with the old API should continue to import `Pkg.BinaryPlatforms`,
Expand Down
1 change: 1 addition & 0 deletions base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export
ComplexF64,
ComplexF32,
ComplexF16,
ComposedFunction,
DenseMatrix,
DenseVecOrMat,
DenseVector,
Expand Down
48 changes: 40 additions & 8 deletions base/operators.jl
Original file line number Diff line number Diff line change
Expand Up @@ -871,26 +871,58 @@ julia> fs = [
julia> ∘(fs...)(3)
3.0
```
See also [`Base.ComposedFunction`](@ref).
"""
function ∘ end

struct ComposedFunction{F,G} <: Function
f::F
g::G
ComposedFunction{F, G}(f, g) where {F, G} = new{F, G}(f, g)
ComposedFunction(f, g) = new{Core.Typeof(f),Core.Typeof(g)}(f, g)
"""
Base.ComposedFunction{Outer,Inner} <: Function

Represents the composition of two callable objects `outer::Outer` and `inner::Inner`. That is
```julia
ComposedFunction(outer, inner)(args...; kw...) === outer(inner(args...; kw...))
```
The preferred way to construct instance of `ComposedFunction` is to use the composition operator [`∘`](@ref):
```jldoctest
julia> sin ∘ cos === Base.ComposedFunction(sin, cos)
true

julia> typeof(sin∘cos)
Base.ComposedFunction{typeof(sin), typeof(cos)}
```
The composed pieces are stored in the fields of `ComposedFunction` and can be retrieved as follows:
```jldoctest
julia> composition = sin ∘ cos
sin ∘ cos

julia> composition.outer === sin
true

julia> composition.inner === cos
true
```
!!! compat "Julia 1.6"
ComposedFunction requires at least Julia 1.6. In earlier versions `∘` returns an anonymous function instead.

See also [`∘`](@ref).
"""
struct ComposedFunction{O,I} <: Function
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, This is coming very late.

Just wanted to find out if it is necessary to restrict O and I to be subtypes of Base.Callable as in

struct ComposedFunction{O<:Base.Callable, I<:Base.Callable} <: Function

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no reason to restrict to Callable here. There are lots of callable objects that aren't a subtype of Callable and this would break those cases.

outer::O
inner::I
ComposedFunction{O, I}(outer, inner) where {O, I} = new{O, I}(outer, inner)
ComposedFunction(outer, inner) = new{Core.Typeof(outer),Core.Typeof(inner)}(outer, inner)
end

(c::ComposedFunction)(x...) = c.f(c.g(x...))
(c::ComposedFunction)(x...) = c.outer(c.inner(x...))

∘(f) = f
∘(f, g) = ComposedFunction(f, g)
∘(f, g, h...) = ∘(f ∘ g, h...)

function show(io::IO, c::ComposedFunction)
show(io, c.f)
show(io, c.outer)
print(io, " ∘ ")
show(io, c.g)
show(io, c.inner)
end

"""
Expand Down
1 change: 1 addition & 0 deletions doc/src/base/base.md
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ Base.invokelatest
new
Base.:(|>)
Base.:(∘)
Base.ComposedFunction
```

## Syntax
Expand Down