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

Add == definition of ComposedOptic and IndexLens from Setfield #146

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/optics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ using CompositionsBase
using Base: getproperty
using Base

import Base: ==

const EXPERIMENTAL = """This function/type is experimental. It can be changed or deleted at any point without warning"""

"""
Expand Down Expand Up @@ -416,6 +418,8 @@ Construct a lens for accessing an element of an object at `indices` via `[]`.
"""
IndexLens(indices::Integer...) = IndexLens(indices)

Base.:(==)(l::IndexLens, r::IndexLens) = l.indices == r.indices
sunxd3 marked this conversation as resolved.
Show resolved Hide resolved

Base.@propagate_inbounds function (lens::IndexLens)(obj)
getindex(obj, lens.indices...)
end
Expand Down Expand Up @@ -486,7 +490,6 @@ Broadcast.broadcastable(

Base.:(!)(f::Union{PropertyLens,IndexLens,DynamicIndexLens}) = (!) ∘ f


function make_salt(s64::UInt64)::UInt
# used for faster hashes. See https://github.com/jw3126/Setfield.jl/pull/162
if UInt === UInt64
Expand Down
65 changes: 65 additions & 0 deletions test/test_core.jl
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,71 @@ Base.show(io::IO, ::MIME"text/plain", ::LensIfTextPlain) =
end
end

@testset "equality & hashing" begin
# singletons (identity and property optic) are egal
for (l1, l2) ∈ [
@optic(_) => @optic(_),
@optic(_.a) => @optic(_.a),
]
@test l1 === l2
@test l1 == l2
@test hash(l1) == hash(l2)
end

# composite and index optics are structurally equal
for (l1, l2) ∈ [
@optic(_[1]) => @optic(_[1]),
@optic(_.a[2]) => @optic(_.a[2]),
@optic(_.a.b[3]) => @optic(_.a.b[3]),
@optic(_[1:10]) => @optic(_[1:10]),
@optic(_.a[2:20]) => @optic(_.a[2:20]),
@optic(_.a.b[3:30]) => @optic(_.a.b[3:30]),
]
@test l1 == l2
@test hash(l1) == hash(l2)
end

# inequality
for (l1, l2) ∈ [
@optic(_[1]) => @optic(_[2]),
@optic(_.a[1]) => @optic(_.a[2]),
@optic(_.a[1]) => @optic(_.b[1]),
@optic(_[1:10]) => @optic(_[2:20]),
@optic(_.a[1:10]) => @optic(_.a[2:20]),
@optic(_.a[1:10]) => @optic(_.b[1:10]),
]
@test l1 != l2
end

# equality with non-equal range types (Setfield #165)
for (l1, l2) ∈ [
@optic(_[1:10]) => @optic(_[Base.OneTo(10)]),
@optic(_.a[1:10]) => @optic(_.a[Base.OneTo(10)]),
@optic(_.a.b[1:10]) => @optic(_.a.b[Base.OneTo(10)]),
@optic(_.a[Base.StepRange(1, 1, 5)].b[1:10]) => @optic(_.a[1:5].b[Base.OneTo(10)]),
@optic(_.a.b[1:3]) => @optic(_.a.b[[1, 2, 3]]),
]
@test l1 == l2
@test hash(l1) == hash(l2)
end
Comment on lines +226 to +236
Copy link
Author

Choose a reason for hiding this comment

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

== for ComposedFunctions are === by https://github.com/JuliaLang/julia/blob/63e365feb8692b9d7cb5298954c26ab7af268171/base/Base.jl#L208, which means other than the first tuple (tests between IndexLens), all will be false because they are not egal.

Copy link
Member

Choose a reason for hiding this comment

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

Maybe create a question/issue for Julia itself?


# Hash property: equality implies equal hashes, or in other terms:
# optics either have equal hashes or are unequal
# Because collisions can occur theoretically (though unlikely), this is a property test,
# not a unit test.
random_optics = (@optic(_.a[rand(Int)]) for _ in 1:1000)
@test all((hash(l2) == hash(l1)) || (l1 != l2)
for (l1, l2) in zip(random_optics, random_optics))

# Optics should hash differently from the underlying tuples, to avoid confusion.
# To account for potential collisions, we check that the property holds with high
# probability.
@test all(hash(@optic(_[i])) != hash((i,)) for i = 1:1000)

# Same for tuples of tuples (√(1000) ≈ 32).
@test all(hash(@optic(_[i][j])) != hash(((i,), (j,))) for i = 1:32, j = 1:32)
end

@testset "type stability" begin
o1 = 2
o22 = 2
Expand Down
Loading