Skip to content

Commit

Permalink
Convert Any.== to a builtin (#3956)
Browse files Browse the repository at this point in the history
`Any.==` is a builtin method. The semantics is the same as it used to be, except that we no longer assume `x == y` iff `Meta.is_same_object x y`, which used to be the case and caused failures in table tests.

# Important Notes
Measurements from `EqualsBenchmarks` shows that the performance of `Any.==` for recursive atoms increased by roughly 20%, and the performance for primitive types stays roughly the same.
  • Loading branch information
Akirathan authored Dec 29, 2022
1 parent 74742d3 commit e6838bc
Show file tree
Hide file tree
Showing 40 changed files with 1,214 additions and 365 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,7 @@
- [Vector returns warnings of individual elements][3938]
- [Enso.getMetaObject, Type.isMetaInstance and Meta.is_a consolidation][3949]
- [Add executionContext/interrupt API command][3952]
- [Any.== is a builtin method][3956]
- [Simplify exception handling for polyglot exceptions][3981]

[3227]: https://github.com/enso-org/enso/pull/3227
Expand Down Expand Up @@ -568,6 +569,7 @@
[3938]: https://github.com/enso-org/enso/pull/3938
[3949]: https://github.com/enso-org/enso/pull/3949
[3952]: https://github.com/enso-org/enso/pull/3952
[3956]: https://github.com/enso-org/enso/pull/3956
[3981]: https://github.com/enso-org/enso/pull/3981

# Enso 2.0.0-alpha.18 (2021-10-12)
Expand Down
35 changes: 14 additions & 21 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Any.enso
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,24 @@ type Any
implementing equality for your own types, keep in mind that it needs to
work with any Enso value as the `that` argument.

! Unicode Equality
The definition of equality includes Unicode canonicalization. I.e. two
texts are equal if they are identical after canonical decomposition. This
ensures that different ways of expressing the same character in the
underlying binary representation are considered equal.

? Generic Equality and Performance
While the generic equality provided here will work for _all_ values in
Enso, its performance may often be suboptimal. Many types can implement
their own equality operations that will be more efficient than these.

> Example
The string 'é' (i.e. the character U+00E9, LATIN SMALL LETTER E WITH ACUTE)
is canonically the same as the string 'e\u0301' (i.e. the letter `e`
followed by U+0301, COMBINING ACUTE ACCENT). Therefore:

('é' == 'e\u0301') == True

> Example
Checking if the variable `a` is equal to `147`.

Expand All @@ -88,27 +101,7 @@ type Any
a = 7 * 21
a == 147
== : Any -> Boolean
== self that = if Meta.is_same_object self that then True else
self_meta = Meta.meta self
that_meta = Meta.meta that
case Pair.new self_meta that_meta of
Pair.Value (Meta.Atom.Value _) (Meta.Atom.Value _) ->
c_1 = self_meta.constructor.value ...
c_2 = that_meta.constructor.value ...
if Meta.is_same_object c_1 c_2 . not then False else
f_1 = self_meta.fields
f_2 = that_meta.fields
0.up_to f_1.length . all i-> (f_1.at i) == (f_2.at i)
Pair.Value (Meta.Error.Value _) (Meta.Error.Value _) -> self_meta.payload == that_meta.payload
Pair.Value (Meta.Polyglot.Value o_1) (Meta.Polyglot.Value o_2) ->
langs_match = (self_meta.get_language == Meta.Language.Java) && (that_meta.get_language == Meta.Language.Java)
if langs_match.not then False else o_1.equals o_2
Pair.Value (Meta.Unresolved_Symbol.Value _) (Meta.Unresolved_Symbol.Value _) ->
(self_meta.name == that_meta.name) && (self_meta.scope == that_meta.scope)
## Constructor comparison is covered by the identity equality.
Primitive objects should define their own equality.
Therefore, there are no more cases to handle in self method.
_ -> False
== self that = @Builtin_Method "Any.=="

## ALIAS Inequality

Expand Down
15 changes: 0 additions & 15 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Data/Array.enso
Original file line number Diff line number Diff line change
Expand Up @@ -154,18 +154,3 @@ type Array
primitive array protocol.
to_array : Array
to_array self = @Builtin_Method "Array.to_array"

## Checks whether this array is equal to `that`.

Arguments:
- that: The array to compare this array against.

Two arrays are considered equal, when they have the same length and
their items are pairwise equal.

== : Array -> Boolean
== self that =
if Meta.is_same_object Array self then Meta.is_same_object Array that else
eq_at i = self.at i == that.at i
Panic.catch Any handler=(_ -> False)
if self.length == that.length then 0.up_to self.length . all eq_at else False
12 changes: 0 additions & 12 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Data/Boolean.enso
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,6 @@ type Boolean
True
False

## Compares two booleans for equality.

Arguments:
- that: The boolean to compare this with.

> Example
Comparing True to False to get False.

True == False
== : Boolean -> Boolean
== self that = @Builtin_Method "Boolean.=="

## Computes the logical and (conjunction) of two booleans.

Arguments:
Expand Down
6 changes: 0 additions & 6 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Data/Locale.enso
Original file line number Diff line number Diff line change
Expand Up @@ -420,9 +420,3 @@ type Locale
example_to_text = Locale.default.to_text
to_text : Text | Nothing
to_text self = self.java_locale.toLanguageTag

## Compares two locales for equality.
== : Any -> Boolean
== self other = case other of
Locale.Value other_java_locale -> self.java_locale.equals other_java_locale
_ -> False
24 changes: 0 additions & 24 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Data/Numbers.enso
Original file line number Diff line number Diff line change
Expand Up @@ -458,18 +458,6 @@ type Decimal
^ : Number -> Number
^ self that = @Builtin_Method "Decimal.^"

## Compares this and that for equality.

Arguments:
- that: The number to compare this against.

> Example
Comparing 7 and 2.1 for equality.

7 == 2.1
== : Number -> Boolean
== self that = @Builtin_Method "Decimal.=="

## Checks if this is greater than that.

Arguments:
Expand Down Expand Up @@ -708,18 +696,6 @@ type Integer
^ : Number -> Number
^ self that = @Builtin_Method "Integer.^"

## Compares this and that for equality.

Arguments:
- that: The number to compare this against.

> Example
Comparing 7 and 2 for equality.

7 == 2
== : Number -> Boolean
== self that = @Builtin_Method "Integer.=="

## Checks if this is greater than that.

Arguments:
Expand Down
23 changes: 0 additions & 23 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Data/Text.enso
Original file line number Diff line number Diff line change
Expand Up @@ -33,29 +33,6 @@ type Text
+ : Text -> Text
+ self that = @Builtin_Method "Text.+"

## Checks whether `self` is equal to `that`.

Arguments:
- that: The text to compare `self` for equality with.

! Unicode Equality
The definition of equality includes Unicode canonicalization. I.e. two
texts are equal if they are identical after canonical decomposition. This
ensures that different ways of expressing the same character in the
underlying binary representation are considered equal.

> Example
The string 'é' (i.e. the character U+00E9, LATIN SMALL LETTER E WITH ACUTE)
is canonically the same as the string 'e\u0301' (i.e. the letter `e`
followed by U+0301, COMBINING ACUTE ACCENT). Therefore:

('é' == 'e\u0301') == True
== : Any -> Boolean
== self that = if Meta.is_same_object self Text then Meta.is_same_object that Text else
case that of
_ : Text -> Text_Utils.equals self that
_ -> False

## Compare two texts to discover their ordering.

Arguments:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -605,14 +605,6 @@ type Date
Ordering.from_sign sign
_ -> Error.throw (Type_Error.Error Date that "that")

## Compares two Dates for equality.
== : Date -> Boolean
== self that = case that of
Date -> Meta.is_same_object self Date
_ : Date ->
sign = Time_Utils.compare_to_localdate self that
0 == sign
_ -> False

## PRIVATE
week_days_between start end =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -701,12 +701,3 @@ type Date_Time
sign = Time_Utils.compare_to_zoneddatetime self that
Ordering.from_sign sign
_ -> Error.throw (Type_Error.Error Date_Time that "that")

## Compares two Date_Time for equality.
== : Date_Time -> Boolean
== self that = case that of
Date_Time -> Meta.is_same_object self Date_Time
_ : Date_Time ->
sign = Time_Utils.compare_to_zoneddatetime self that
0 == sign
_ -> False
Original file line number Diff line number Diff line change
Expand Up @@ -166,23 +166,6 @@ type Duration
Panic.catch ArithmeticException (self.minus_builtin that) err->
Error.throw (Time_Error.Error err.payload.getMessage)

## Check two durations for equality.

Arguments:
- that: The duration to compare against `self`.

> Examples
Check if 60 seconds and 1 minute are equal.

import Standard.Base.Data.Time.Duration

example_eq = (Duration.new seconds=60).total_minutes == (Duration.new minutes=1).total_minutes
== : Duration -> Boolean
== self that =
case that of
_ : Duration -> self.equals_builtin that
_ -> False

## Compares `self` to `that` to produce an ordering.

Arguments:
Expand Down
14 changes: 0 additions & 14 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Period.enso
Original file line number Diff line number Diff line change
Expand Up @@ -129,20 +129,6 @@ type Period
DateTimeException -> Error.throw Time_Error.Error "Period subtraction failed"
ArithmeticException -> Error.throw Illegal_Argument.Error "Arithmetic error"

## Check two periods for equality.

Note that two periods are equal if they have the exact same amount of
years, months, and days. So `(Period.new days=30)` and
`(Period.new months=1)` are not equal. Even `(Period.new years=1)` and
`(Period.new months=12)` are not equal.

Arguments:
- other_period: The period to compare against `self`.
== : Period -> Boolean
== self that =
ensure_period that <|
self.internal_period.equals that.internal_period

## Just throws `Incomparable_Values`, because periods cannot be
compared without additional context.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -377,12 +377,3 @@ type Time_Of_Day
sign = Time_Utils.compare_to_localtime self that
Ordering.from_sign sign
_ -> Error.throw (Type_Error.Error Time_Of_Day that "that")

## Compares two Time_Of_Day for equality.
== : Date -> Boolean
== self that = case that of
Time_Of_Day -> Meta.is_same_object self Time_Of_Day
_ : Time_Of_Day ->
sign = Time_Utils.compare_to_localtime self that
0 == sign
_ -> False
Original file line number Diff line number Diff line change
Expand Up @@ -169,10 +169,3 @@ type Time_Zone
type_pair = ["type", "Time_Zone"]
cons_pair = ["constructor", "new"]
JS_Object.from_pairs [type_pair, cons_pair, ["id", self.zone_id]]

## Compares two Zones for equality.
== : Time_Zone -> Boolean
== self that =
case that of
_ : Time_Zone -> Time_Utils.equals_zone self that
_ -> False
22 changes: 0 additions & 22 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Data/Vector.enso
Original file line number Diff line number Diff line change
Expand Up @@ -602,28 +602,6 @@ type Vector a
"and " + remaining_count.to_text + " more elements"
prefix.map .to_text . join ", " "[" " "+remaining_text+"]"

## Checks whether this vector is equal to `that`.

Arguments:
- that: The vector to compare this vector against.

Two vectors are considered equal, when they have the same length and
their items are pairwise equal.

> Example
Compare two vectors for equality (this case is false).

[1, 2, 3] == [2, 3, 4]
== : Vector -> Boolean
== self that = case that of
_ : Vector ->
eq_at i = self.at i == that.at i
if self.length == that.length then 0.up_to self.length . all eq_at else False
_ : Array ->
eq_at i = self.at i == that.at i
if self.length == that.length then 0.up_to self.length . all eq_at else False
_ -> False

## Concatenates two vectors, resulting in a new vector, containing all the
elements of `self`, followed by all the elements of `that`.

Expand Down
7 changes: 0 additions & 7 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Error.enso
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,3 @@ type Error
1.is_error
is_error : Boolean
is_error self = True

## PRIVATE
TODO this is a kludge until we have proper eigentypes and statics.
Allows to check equality of the `Error` type with itself.
== self that = if Meta.is_error self then self else
if Meta.is_error that then that else
Meta.is_same_object self that
11 changes: 0 additions & 11 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Network/URI.enso
Original file line number Diff line number Diff line change
Expand Up @@ -208,14 +208,3 @@ type URI
type_pair = ["type", "URI"]
cons_pair = ["constructor", "parse"]
JS_Object.from_pairs [type_pair, cons_pair, ["uri", self.to_text]]

## Check if this URI is equal to another URI.

> Example
Check if two URIs are equal.

import Standard.Base.Network.URI.URI

example_eq = "https://example.com".to_uri == "http://example.org".to_uri
== : URI -> Boolean
== self that = self.internal_uri.equals that.internal_uri
11 changes: 0 additions & 11 deletions distribution/lib/Standard/Base/0.0.0-dev/src/System/File.enso
Original file line number Diff line number Diff line change
Expand Up @@ -427,17 +427,6 @@ type File
normalize : File
normalize self = @Builtin_Method "File.normalize"

## Checks if this file has the same `path` as `that`.

> Example
Check if two files are equivalent.

import Standard.Examples

example_eq = Examples.csv == Examples.scratch_file
== : File -> Boolean
== self that = @Builtin_Method "File.=="

## Deletes the file.

If the file is a directory, it must be empty, otherwise a `Panic` will
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ check_integrity entity1 entity2 =
- entity2: The entity to check against the first.
check_connection : (Table | Column) -> (Table | Column) -> Boolean
check_connection entity1 entity2 =
entity1.connection == entity2.connection
Meta.is_same_object entity1.connection entity2.connection

## PRIVATE

Expand Down
2 changes: 1 addition & 1 deletion docs/distribution/launcher.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ using GitHub API, similarly as Enso releases.
The primary purpose of the launcher is running various Enso components, namely
the REPL, running a project, Enso scripts or the language server.

The launcher automatically infers which Enso version to used, based on the
The launcher automatically infers which Enso version to use, based on the
parameters and configuration:

- When running a project or the language server, the version specified in
Expand Down
Loading

0 comments on commit e6838bc

Please sign in to comment.