enum should use `===` to compare equivalence
Roger-luo opened this issue · 7 comments
enum should use a different function to compare equivalence, otherwise the following code will stack overflow, even it's a valid code
@enum Fruit begin
Apple
Banana
Orange
end
function Base.:(==)(lhs::Fruit, rhs::Fruit)
@match (lhs, rhs) begin
(&Apple, &Apple) => true
(&Banana, &Banana) => true
(&Orange, &Orange) => true
_ => false
end
end
Apple == Apple
This is causing issue for a more complicated case
We should make these enums also MLStyle-recognized enums:
using MLStyle
using MLStyle.AbstractPatterns: literal
@enum Fruit begin
Apple
Banana
Orange
end
MLStyle.is_enum(::Fruit) = true
MLStyle.pattern_uncall(e::Fruit, _, _, _, _) = literal(e)
function Base.:(==)(lhs::Fruit, rhs::Fruit)
@match (lhs, rhs) begin
(Apple, Apple) => true
(Banana, Banana) => true
(Orange, Orange) => true
_ => false
end
end
Apple == Apple # true
Basically, &
uses ==
equality. Customizing the &
's behavior via custom ==
is intentional.
Ok, but this problem exists for user-defined types, e.g the ADT in Expronicon has the following issue
using MLStyle
using Expronicon.ADT: @adt
@adt Foo begin
Bar
struct Baz
args::Vector{Int}
end
end
function Base.:(==)(lhs::Foo, rhs::Foo)
@match (lhs, rhs) begin
(Bar, Bar) => true
(Baz(args), Baz(args)) => args == args
_ => false
end
end
Bar == Bar
where Bar
is marked as is_enum
and literal
, but because this calls ==
to compare the variants of Foo
this will cause a dispatch error.
I got it, this is a bug.
Bar::Foo
is not a bitstype instance, so ==
is used. This is a bug, as other pattern matching languages would use tag
s to compare ADT cases instead of using equality.
===
will not work, because ===
gives false
does not really imply unmatch. Enums are only semantically immutable (i.e., structural equality). However, mutable values that are semantically immutable inside the enum might not well conform ===
(e.g., String
s in early Julia version, IIRC <1.1, or any custom mutable values that defines ==
for semantically immutable).
Considering the same issue in other PM programming languages (not Python), we can find that equality are separated from pattern matching (unless for bit-only literal values). Your example is also a good pro to this.
I'm thinking about how to address this issue.
Using tag
s to compare enums can implemented in future for more legal semantics.
However, currently I need the fix to be backward-compatible.
Cases for how to translate "comparing via tag
s for enum values" into julia:
@match V begin
P => ...
_ => ...
end
- if
P
is bit-only, we compare it withV
via===
(supported by the current mechanism) - if
P
is immutable (not bit-only, so may contains references), we check ifP
andV
has the same type, and if their corresponding fields are equal by==
. (NOTE that the recursive comparison is not===
, so we CAN't use===
here) - if
P
is mutable, it's not a valid enum.
working on this.