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 Comparator conversion for all types #4067

Merged
merged 68 commits into from
Feb 10, 2023
Merged
Show file tree
Hide file tree
Changes from 56 commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
a170e53
Copy initial sources from jtulach/DefaultCustomEq branch
Akirathan Jan 19, 2023
91c227d
Comparable.from returns union of three types
Akirathan Jan 23, 2023
7e4df51
Fix Eq.assert_ordered_comparators.
Akirathan Jan 23, 2023
5b5fc70
Hide hash_code method as much as possible - Move it to Meta.hash_code
Akirathan Jan 23, 2023
f9c9bdd
When comparators are not of same type, comparison throws Incomparable…
Akirathan Jan 23, 2023
d12c485
Any.== uses comparator pattern. Any.< is a builtin
Akirathan Jan 24, 2023
93ed516
Refactor Eq_Spec to the newest comparator pattern.
Akirathan Jan 24, 2023
1399502
Merge branch 'develop' into wip/akirathan/custom-eq-183945328
Akirathan Jan 24, 2023
803ffb1
Any.== uses equals_builtin to avoid infinite recursion
Akirathan Jan 24, 2023
3d76e9f
Implement LessThanAnyNode.build - fix for generated code.
Akirathan Jan 24, 2023
f4e12f8
Text has Default_Ordered_Comparator
Akirathan Jan 25, 2023
2958fa8
Fix a small bug in Type.getMetaParents
Akirathan Jan 25, 2023
0bfde33
Comparable and default comparators are builtin types
Akirathan Jan 26, 2023
468691b
Implement Comparable.equals_builtin and Comparable.less_builtin
Akirathan Jan 26, 2023
3a099b3
Implement special handling for both EqualsNode and HashCodeNode once …
Akirathan Jan 26, 2023
f75b5c1
Handle Comparable.from conversion for numbers in Numbers module
Akirathan Jan 26, 2023
ed03031
Migrate Equals_Spec to the new comparator API
Akirathan Jan 26, 2023
c971644
Cosmetics
Akirathan Jan 26, 2023
c0e08e9
Fix Map visualization
Akirathan Jan 26, 2023
fafd285
Rename EqualsAnyNode to EqualsNode
Akirathan Jan 26, 2023
07e79d9
Rename LessThanAnyNode to LessThanNode
Akirathan Jan 27, 2023
5087323
EqualsNode and LessThanNode is a static builtin method
Akirathan Jan 27, 2023
c1699ea
Handle cases when there is no conversion to Comparable in Any.==
Akirathan Jan 27, 2023
bd2a5ff
Add specializations for types, files, modules and interop values with…
Akirathan Jan 27, 2023
f293752
Array.sort respect new comparator API
Akirathan Jan 27, 2023
99c41cb
Improve signature of operators on Any
Akirathan Jan 27, 2023
c6d5628
Fix some warnings
Akirathan Jan 27, 2023
848a782
[WIP] Migrate Table_Spec to the new comparator API
Akirathan Jan 27, 2023
39f18d5
Merge branch 'develop' into wip/akirathan/custom-eq-183945328
Akirathan Jan 27, 2023
46a4419
Fix some specializations of Eq/Hash nodes.
Akirathan Jan 30, 2023
6cbdabc
Replace compare_to with Comparators (Batch 1)
Akirathan Jan 30, 2023
c2fff5e
Improve some docs
Akirathan Jan 30, 2023
548920b
Replace compare_to with Comparators (Batch 2)
Akirathan Jan 31, 2023
641c9c8
Replace compare_to with Comparators (Batch 3)
Akirathan Feb 2, 2023
1bb1d9b
Fix infinite recursion of Any.==
Akirathan Feb 2, 2023
92ae88e
LessThanNode fallback returns Type_Error
Akirathan Feb 2, 2023
bc0c155
Fix conversion method dispatch on polyglot values
Akirathan Feb 2, 2023
37a3744
Add tests for comparable conversion for polyglot objects
Akirathan Feb 3, 2023
8637de0
Add some specializations to InvokeConversionNode
Akirathan Feb 3, 2023
3bf8738
Some test fixes
Akirathan Feb 3, 2023
e06b12d
Add Java zoned date time to ValuesGenerator
Akirathan Feb 3, 2023
897118c
Use Meta.is_same_object for equality of types in Any methods
Akirathan Feb 6, 2023
9c0ac84
Move ConversionMethodTests to runtime-polyglot
Akirathan Feb 7, 2023
ed01a1b
Add Any.== to micro-distribution
Akirathan Feb 7, 2023
d470317
Fix ParseStdLibTests
Akirathan Feb 7, 2023
6344a8c
Add JS_Object_Comparator
Akirathan Feb 7, 2023
6dbbf67
Fix some std lib tests
Akirathan Feb 7, 2023
bf06916
Make nodes in tests static
Akirathan Feb 7, 2023
90212ba
Add some date values to ValuesGenerator
Akirathan Feb 7, 2023
67267e4
Fix some specs in EqualsNode and HashCodeNode
Akirathan Feb 7, 2023
e181336
fmt
Akirathan Feb 7, 2023
4218bc4
Merge branch 'develop' into wip/akirathan/custom-eq-183945328
Akirathan Feb 7, 2023
34dcc0f
Do not check hash codes in Any.==
Akirathan Feb 8, 2023
d23d1fa
Update changelog
Akirathan Feb 8, 2023
2bf5256
Import Any in benchmarks failing on "No such method ==".
Akirathan Feb 8, 2023
4808516
Merge branch 'develop' into wip/akirathan/custom-eq-183945328
Akirathan Feb 8, 2023
bddb09f
Comparable, and Default_(Un)Ordered_Comparator are "hidden" builtins
Akirathan Feb 8, 2023
229ed44
Address some nitpicks from review
Akirathan Feb 8, 2023
cb12a66
Temporarily ignore `Any == Boolean` tests.
Akirathan Feb 8, 2023
867a30e
Merge branch 'develop' into wip/akirathan/custom-eq-183945328
Akirathan Feb 8, 2023
610eaf2
import Default_Unordered_Comparator in Map_Spec
JaroslavTulach Feb 9, 2023
731b10e
Small fix in Vector_Spec
Akirathan Feb 9, 2023
001e792
Remove Eq_Spec
Akirathan Feb 9, 2023
c3cb84f
Revert "Fix Map visualization"
Akirathan Feb 9, 2023
534a557
Merge branch 'develop' into wip/akirathan/custom-eq-183945328
Akirathan Feb 9, 2023
658af5a
fmt
Akirathan Feb 9, 2023
e7d0114
Merge branch 'develop' into wip/akirathan/custom-eq-183945328
Akirathan Feb 9, 2023
2305fb2
Merge branch 'develop' into wip/akirathan/custom-eq-183945328
mergify[bot] Feb 9, 2023
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,7 @@
- [Optimize Atom storage layouts][3862]
- [Make instance methods callable like statics for builtin types][4077]
- [Convert large longs to doubles, safely, for host calls][4099]
- [Consistent ordering with comparators](4067)
- [Profile engine startup][4110]

[3227]: https://github.com/enso-org/enso/pull/3227
Expand Down Expand Up @@ -647,6 +648,7 @@
[4056]: https://github.com/enso-org/enso/pull/4056
[4077]: https://github.com/enso-org/enso/pull/4077
[4099]: https://github.com/enso-org/enso/pull/4099
[4067]: https://github.com/enso-org/enso/pull/4067
[4110]: https://github.com/enso-org/enso/pull/4110

# Enso 2.0.0-alpha.18 (2021-10-12)
Expand Down
88 changes: 64 additions & 24 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Any.enso
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import project.Data.Ordering.Ordering
# We have to import also conversion methods from Ordering, therefore, we import all
from project.Data.Ordering import all
import project.Data.Pair.Pair
import project.Data.Range.Extensions
import project.Data.Text.Text
import project.Error.Error
import project.Error.Incomparable_Values.Incomparable_Values
import project.Error.Common.No_Such_Conversion
import project.Error.Common.Type_Error
import project.Nothing.Nothing
import project.Meta
import project.Panic.Panic

from project.Data.Boolean import Boolean, True, False

Expand Down Expand Up @@ -101,7 +106,21 @@ type Any
a = 7 * 21
a == 147
== : Any -> Boolean
== self that = @Builtin_Method "Any.=="
== self that =
# If there is No_Such_Conversion, then `self` and `that` are probably
# host or polyglot values, so we just compare them with the default comparator.
eq_self = Panic.catch No_Such_Conversion (Comparable.from self) _-> Default_Unordered_Comparator
eq_that = Panic.catch No_Such_Conversion (Comparable.from that) _-> Default_Unordered_Comparator
if Meta.is_same_object eq_self Incomparable then False else
similar_type = Meta.is_same_object eq_self eq_that
if similar_type.not then False else
case eq_self.is_ordered of
True ->
# Comparable.equals_builtin is a hack how to directly access EqualsNode from the
# engine, so that we don't end up in an infinite recursion here (which would happen
# if we would compare with `eq_self == eq_that`).
Comparable.equals_builtin (eq_self.compare self that) Ordering.Equal
False -> eq_self.equals self that

## ALIAS Inequality

Expand Down Expand Up @@ -133,12 +152,8 @@ type Any
Arguments:
- that: The value to compare `self` against.

To have `>` defined, a type must define `compare_to`, returning an Ordering.

! Implementing Greater Than
Many types can admit a definition of greater than that is more efficient
than the generic one given here. When implementing this for your own types
please ensure that it is semantically equivalent to using `.compare_to`.
To have `>` properly defined, a type must have an associated ordered comparator.
See `Ordering.enso` for information how comparators work.

> Example
Checking if the variable `a` is greater than `147`.
Expand All @@ -148,8 +163,12 @@ type Any
example_greater =
a = 7 * 28
a > 147
> : Any -> Boolean
> self that = self.compare_to that == Ordering.Greater
> : Any -> Boolean ! Incomparable_Values
> self that =
assert_ordered_comparators self that <|
case (Comparable.from self).compare self that of
Ordering.Greater -> True
_ -> False

## ALIAS Greater Than or Equal

Expand All @@ -174,10 +193,13 @@ type Any
example_greater_eq =
a = 6 * 21
a >= 147
>= : Any -> Boolean
>= : Any -> Boolean ! Incomparable_Values
>= self that =
ordering = self.compare_to that
(ordering == Ordering.Greater) || (ordering == Ordering.Equal)
assert_ordered_comparators self that <|
case (Comparable.from self).compare self that of
Ordering.Less -> False
Ordering.Equal -> True
Ordering.Greater -> True

## ALIAS Less Than

Expand All @@ -186,12 +208,8 @@ type Any
Arguments:
- that: The value to compare `self` against.

To have `<` defined, a type must define `compare_to`, returning an Ordering.

! Implementing Less Than
Many types can admit a definition of less than that is more efficient than
the generic one given here. When implementing this for your own types
please ensure that it is semantically equivalent to using `.compare_to`.
To have `<` properly defined, a type must have an associated ordered comparator.
See `Ordering.enso` for information how comparators work.

> Example
Checking if the variable `a` is less than `147`.
Expand All @@ -201,8 +219,12 @@ type Any
example_less =
a = 7 * 21
a < 147
< : Any -> Boolean
< self that = self.compare_to that == Ordering.Less
< : Any -> Boolean ! Incomparable_Values
< self that =
assert_ordered_comparators self that <|
case (Comparable.from self).compare self that of
Ordering.Less -> True
_ -> False

## ALIAS Less Than or Equal

Expand All @@ -227,10 +249,13 @@ type Any
example_less_eq =
a = 7 * 21
a < 147
<= : Any -> Boolean
<= : Any -> Boolean ! Incomparable_Values
<= self that =
ordering = self.compare_to that
(ordering == Ordering.Less) || (ordering == Ordering.Equal)
assert_ordered_comparators self that <|
case (Comparable.from self).compare self that of
Ordering.Less -> True
Ordering.Equal -> True
Ordering.Greater -> False

## Checks if the type is an instance of `Nothing`.

Expand Down Expand Up @@ -396,3 +421,18 @@ type Any
(+1 >> *2) 2
>> : (Any -> Any) -> (Any -> Any) -> Any -> Any
>> self ~that = x -> that (self x)


## PRIVATE
Checks if both comparators of the given objects are both of same type and ordered.
If they are not of same type, a `Type_Error` is thrown.
If the comparators are either `Incomparable`, or unordered, `False` is returned.
assert_ordered_comparators : Any -> (Any -> Any) -> Any ! (Type_Error | Incomparable_Values)
assert_ordered_comparators this that ~action =
comp_this = Comparable.from this
comp_that = Comparable.from that
if (Meta.is_same_object comp_this comp_that).not then Error.throw (Type_Error.Error (Meta.type_of comp_this) comp_that "comp_that") else
if Meta.is_same_object comp_this Incomparable || comp_this.is_ordered.not then Error.throw Incomparable_Values else
action


Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ type Array
## Sorts the Array.

Arguments:
- comparator: A comparison function that takes two elements and returns
- compare_func: A comparison function that takes two elements and returns
an Ordering that describes how the first element is ordered with
respect to the second.

Expand All @@ -146,8 +146,8 @@ type Array

[3,2,1].to_array.sort
sort : (Any -> Any -> Ordering) -> Array
sort self comparator=(_.compare_to _) =
self.sort_builtin comparator
sort self compare_func=(Ordering.compare _ _) =
self.sort_builtin compare_func

## Identity.

Expand Down
18 changes: 5 additions & 13 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Data/Boolean.enso
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import project.Any.Any
import project.Data.Ordering.Ordering
import project.Data.Ordering.Comparable
import project.Data.Ordering.Default_Ordered_Comparator
import project.Nothing.Nothing

from project.Data.Boolean.Boolean import True, False
Expand Down Expand Up @@ -55,19 +57,6 @@ type Boolean
not : Boolean
not self = @Builtin_Method "Boolean.not"

## Compares the two operands to determine the ordering of this with
respect to that.

Arguments:
- that: The operand to order this with respect to.

> Example
Computing the ordering of True and False

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

## The if-then-else control flow operator that executes one of two branches
based on a conditional.

Expand Down Expand Up @@ -100,3 +89,6 @@ type Boolean
if (27 % 3) == 0 then IO.println "Fizz"
if_then : Any -> Any | Nothing
if_then self ~on_true = @Builtin_Method "Boolean.if_then"


Comparable.from (_:Boolean) = Default_Ordered_Comparator
38 changes: 24 additions & 14 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Data/Json.enso
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ import project.Warning.Warning
from project.Metadata.Widget import Single_Choice
from project.Metadata.Choice import Option
import project.Metadata.Display

# We need to import conversion methods from Ordering, therefore, we import all
from project.Data.Ordering import all
from project.Data.Boolean import Boolean, True, False

## Methods for serializing from and to JSON.
Expand Down Expand Up @@ -174,19 +175,6 @@ type JS_Object
to_json : Text
to_json self = self.to_text

## Checks if this JS_Object is equal to another JS_Object.

Arguments:
- that: The map to compare `self` to.
== : JS_Object -> Boolean
== self that = case that of
_ : JS_Object ->
self_keys = self.field_names
that_keys = that.field_names
self_keys.length == that_keys.length && self_keys.all key->
(self.get key == that.at key).catch No_Such_Key.Error _->False
_ -> False

## UNSTABLE

Transform the vector into text for displaying as part of its default
Expand All @@ -195,6 +183,28 @@ type JS_Object
to_default_visualization_data self =
render self

type JS_Object_Comparator
is_ordered : Boolean
is_ordered = False

equals : JS_Object -> JS_Object -> Boolean
equals obj1 obj2 =
obj1_keys = obj1.field_names
obj2_keys = obj2.field_names
obj1_keys.length == obj2_keys.length && obj1_keys.all key->
(obj1.get key == obj2.at key).catch No_Such_Key.Error _->False

hash : JS_Object -> Integer
hash obj =
values_hashes = obj.field_names.map field_name->
val = obj.get field_name
Comparable.from val . hash val
# Return sum, as we don't care about ordering of field names
values_hashes.fold 0 (+)

Comparable.from (_:JS_Object) = JS_Object_Comparator


## PRIVATE
Render the JS_Object to Text with truncated depth.
render object depth=0 max_depth=5 max_length=100 = case object of
Expand Down
30 changes: 4 additions & 26 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Data/Numbers.enso
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import project.Data.Ordering.Ordering
import project.Data.Ordering.Comparable
import project.Data.Ordering.Default_Ordered_Comparator
import project.Data.Text.Text
import project.Data.Locale.Locale
import project.Error.Common.Arithmetic_Error
Expand Down Expand Up @@ -529,19 +531,6 @@ type Decimal
ceil : Integer
ceil self = @Builtin_Method "Decimal.ceil"

## Compares the two operands to determine the ordering of this with
respect to that.

Arguments:
- that: The operand to order this with respect to.

> Example
Computing the ordering of 1.732 and 4 (Less).

1.732.compare_to 4
compare_to : Number -> Ordering
compare_to self that = @Builtin_Method "Decimal.compare_to"

## Computes the nearest integer below this decimal.

This method provides a means of converting a Decimal to an Integer.
Expand Down Expand Up @@ -768,19 +757,6 @@ type Integer
ceil : Integer
ceil self = @Builtin_Method "Integer.ceil"

## Compares the two operands to determine the ordering of this with
respect to that.

Arguments:
- that: The operand to order this with respect to.

> Example
Computing the ordering of 1 and 4 (Less).

1.compare_to 4
compare_to : Number -> Ordering
compare_to self that = @Builtin_Method "Integer.compare_to"

## Computes the integer division of this by that.

Arguments:
Expand Down Expand Up @@ -964,6 +940,8 @@ type Integer

parse_builtin text radix = @Builtin_Method "Integer.parse"

Comparable.from (_:Number) = Default_Ordered_Comparator

## UNSTABLE

A syntax error when parsing a double.
Expand Down
Loading