diff --git a/base/operators.jl b/base/operators.jl index 4a9daf21c4be5..1ef02c5459ee1 100644 --- a/base/operators.jl +++ b/base/operators.jl @@ -1094,6 +1094,19 @@ unwrap_composed(c) = (maybeconstructor(c),) call_composed(fs, x, kw) = (@inline; fs[1](call_composed(tail(fs), x, kw))) call_composed(fs::Tuple{Any}, x, kw) = fs[1](x...; kw...) +function hash(f::ComposedFunction, h::UInt)::UInt + h = hash(f.inner, h) + hash(f.outer, h) +end + +function isequal(f1::ComposedFunction, f2::ComposedFunction)::Bool + isequal(f1.inner, f2.inner) && isequal(f1.outer, f2.outer) +end + +function ==(f1::ComposedFunction, f2::ComposedFunction)::Bool + ==(f1.inner, f2.inner) && ==(f1.outer, f2.outer) +end + struct Constructor{F} <: Function end (::Constructor{F})(args...; kw...) where {F} = (@inline; F(args...; kw...)) maybeconstructor(::Type{F}) where {F} = Constructor{F}() diff --git a/test/operators.jl b/test/operators.jl index d97db15def80f..c20fefa87c9c5 100644 --- a/test/operators.jl +++ b/test/operators.jl @@ -201,6 +201,37 @@ end end +@testset "ComposedFunction hash eq" begin + @test sin∘cos == sin∘cos + @test isequal(sin∘cos, sin∘cos) + @test !isequal(sin∘cos, sin∘asin) + @test ==(sin∘cos, sin∘cos) + @test !==(sin∘cos, sin∘asin) + @test hash(sin∘cos) == hash(sin∘cos) + @test hash(sin∘cos) != hash(sin∘tan) + + Base.@kwdef mutable struct F + isequal::Bool = false + eq::Bool = false + end + function Base.isequal(f1::F, f2::F) + f1.isequal + end + function Base.:(==)(f1::F, f2::F) + f1.eq + end + f2 = F() ∘ F() + @test isequal(F(isequal=true) ∘ F(isequal=true) , f2) + @test !isequal(F(isequal=true) ∘ F(isequal=false) , f2) + @test !isequal(F(isequal=false) ∘ F(isequal=true) , f2) + @test !isequal(F(isequal=false) ∘ F(isequal=false) , f2) + + @test ==(F(eq=true) ∘ F(eq=true) , f2) + @test !==(F(eq=true) ∘ F(eq=false) , f2) + @test !==(F(eq=false) ∘ F(eq=true) , f2) + @test !==(F(eq=false) ∘ F(eq=false) , f2) +end + @testset "Nested ComposedFunction's stability" begin f(x) = (1, 1, x...) g = (f ∘ (f ∘ f)) ∘ (f ∘ f ∘ f)