Skip to content

Commit

Permalink
Implement type_of (#3722)
Browse files Browse the repository at this point in the history
This change implements a simple `type_of` method that returns a type of a given value, including for polyglot objects.

The change also allows for pattern matching on various time-related instances. It is a nice-to-have on its own, but it was primarily needed here to write some tests. For equality checks on types we currently can't use `==` due to a known _feature_ which essentially does wrong dispatching. This will be improved in the upcoming statics PR so we agreed that there is no point in duplicating that work and we can replace it later.

Also, note that this PR changes `Meta.is_same_object`. Comparing types revealed that it was wrong when comparing polyglot wrappers over the same value.
  • Loading branch information
hubertp authored Sep 26, 2022
1 parent 98ace6a commit 7a6ee0c
Show file tree
Hide file tree
Showing 24 changed files with 617 additions and 53 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,8 @@
- [Added `databases`, `schemas`, `tables` support to database Connection.][3632]
- [Implemented `start_of` and `end_of` methods for date/time types allowing to
find start and end of a period of time containing the provided time.][3695]
- [Implemented `type_of` and `is_of_type` methods for getting the type of a
value and comparing types, respectively.][3722]
- [Implemented `work_days_until` for counting work dys between dates and
`add_work_days` which allows to shift a date by a number of work days.][3726]
- [Added `query` and `read` functions to Database connections.][3727]
Expand Down Expand Up @@ -320,6 +322,7 @@
[3684]: https://github.com/enso-org/enso/pull/3684
[3691]: https://github.com/enso-org/enso/pull/3691
[3695]: https://github.com/enso-org/enso/pull/3695
[3722]: https://github.com/enso-org/enso/pull/3722
[3726]: https://github.com/enso-org/enso/pull/3726
[3727]: https://github.com/enso-org/enso/pull/3727
[3733]: https://github.com/enso-org/enso/pull/3733
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -502,8 +502,11 @@ type Date
## Compares two Dates for equality.
== : Date -> Boolean
== self that =
sign = Time_Utils.compare_to_localdate self that
0 == sign
case that of
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 @@ -564,5 +564,9 @@ type Date_Time
## Compares two Date_Time for equality.
== : Date_Time -> Boolean
== self that =
sign = Time_Utils.compare_to_zoneddatetime self that
0 == sign
case that of
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 @@ -330,5 +330,9 @@ type Time_Of_Day
## Compares two Time_Of_Day for equality.
== : Date -> Boolean
== self that =
sign = Time_Utils.compare_to_localtime self that
0 == sign
case that of
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 @@ -140,4 +140,7 @@ type Time_Zone

## Compares two Zones for equality.
== : Time_Zone -> Boolean
== self that = Time_Utils.equals_zone self that
== self that =
case that of
Time_Zone -> Time_Utils.equals_zone self that
_ -> False
41 changes: 18 additions & 23 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Meta.enso
Original file line number Diff line number Diff line change
Expand Up @@ -253,12 +253,13 @@ is_same_object value_1 value_2 = @Builtin_Method "Meta.is_same_object"
## UNSTABLE
ADVANCED

Checks if `self` is an instance of `typ`.
Checks whether `self` represents the same underlying reference as `value`.

Arguments:
- typ: The type to check `self` against.
Any.is_a : Any -> Boolean
Any.is_a self typ = is_a self typ
- value_1: The first value.
- value_2: The second value.
Any.is_same_object_as : Any -> Boolean
Any.is_same_object_as self value = is_same_object self value

## UNSTABLE
ADVANCED
Expand All @@ -267,8 +268,8 @@ Any.is_a self typ = is_a self typ

Arguments:
- typ: The type to check `self` against.
Any.is_an : Any -> Boolean
Any.is_an self typ = is_a self typ
Any.is_a : Any -> Boolean
Any.is_a self typ = is_a self typ

## UNSTABLE
ADVANCED
Expand All @@ -278,17 +279,7 @@ Any.is_an self typ = is_a self typ
Arguments:
- typ: The type to check `self` against.
Base.Error.is_a : Any -> Boolean
Base.Error.is_a self typ = self.is_an typ

## UNSTABLE
ADVANCED

Checks if `self` is an instance of `typ`.

Arguments:
- typ: The type to check `self` against.
Base.Error.is_an : Any -> Boolean
Base.Error.is_an self typ = typ==Any || typ==Base.Error
Base.Error.is_a self typ = typ==Any || typ==Base.Error

## UNSTABLE
ADVANCED
Expand All @@ -303,12 +294,17 @@ is_a value typ = if is_same_object value typ then True else
if typ == Any then True else
if is_error value then typ == Base.Error else
case value of
Array -> typ == Array
Vector.Vector -> typ.is_same_object_as Vector.Vector
Array -> typ.is_same_object_as Array
Boolean -> if typ == Boolean then True else value == typ
Text -> typ == Text
Number -> if typ == Number then True else case value of
Integer -> typ == Integer
Decimal -> typ == Decimal
Date_Time.Date_Time -> typ.is_same_object_as Date_Time.Date_Time
Date.Date -> typ.is_same_object_as Date.Date
Time_Of_Day.Time_Of_Day -> typ.is_same_object_as Time_Of_Day.Time_Of_Day
Time_Zone.Time_Zone -> typ.is_same_object_as Time_Zone.Time_Zone
Base.Polyglot ->
typ==Base.Polyglot || java_instance_check value typ
_ ->
Expand All @@ -335,13 +331,12 @@ java_instance_check value typ =
## UNSTABLE
ADVANCED

Checks if `value` is an instance of `typ`.
Returns the type of the given value.

Arguments:
- value: The value to check for being an instance of `typ`.
- typ: The type to check `self` against.
is_an : Any -> Any -> Boolean
is_an value typ = is_a value typ
- value: the value to get the type of.
type_of : Any -> Any
type_of value = @Builtin_Method "Meta.type_of_builtin"

## Represents a polyglot language.
type Language
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -705,7 +705,7 @@ type Table
agg = p.second
new_name = p.first
Aggregate_Helper.make_aggregate_column self agg new_name . catch
partitioned = results.partition (_.is_an Internal_Column_Data)
partitioned = results.partition (_.is_a Internal_Column_Data)
## When working on join we may encounter further issues with having
aggregate columns exposed directly, it may be useful to re-use
the `lift_aggregate` method to push the aggregates into a
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ type Excel_Range
unchanged.
column_index : (Text|Integer) -> Integer
column_index column =
if column.is_an Integer then column else Java_Range.parseA1Column column
if column.is_a Integer then column else Java_Range.parseA1Column column

## Creates a Range from an address.
from_address : Text -> Excel_Range
Expand Down
44 changes: 44 additions & 0 deletions distribution/lib/Standard/Test/0.0.0-dev/src/Main.enso
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,28 @@ Any.should_equal self that frames_to_skip=0 = case self == that of
msg = self.to_text + " did not equal " + that.to_text + " (at " + loc + ")."
fail msg

## Asserts that `self` value is equal to the expected type value.

Arguments:
- that: The type to check `self` for equality with.
- frames_to_skip (optional, advanced): used to alter the location which is
displayed as the source of this error.

> Example
Assert that some type is equal to another.,

import Standard.Examples
import Standard.Test

example_should_equal = Examples.some_tpe . should_equal_tpe Vector.Vector
Any.should_equal_type : Any -> Integer -> Assertion
Any.should_equal_type self that frames_to_skip=0 = case (self.is_same_object_as that) of
True -> Success
False ->
loc = Meta.get_source_location 2+frames_to_skip
msg = self.to_text + " did not equal type " + that.to_text + " (at " + loc + ")."
fail msg

## Asserts that `self` value is not equal to the expected value.

Arguments:
Expand All @@ -308,6 +330,28 @@ Any.should_not_equal self that frames_to_skip=0 = case self != that of
msg = self.to_text + " did equal " + that.to_text + " (at " + loc + ")."
fail msg

## Asserts that `self` value is not equal to the expected type value.

Arguments:
- that: The type to check `self` for equality with.
- frames_to_skip (optional, advanced): used to alter the location which is
displayed as the source of this error.

> Example
Assert that some type is equal to another.,

import Standard.Examples
import Standard.Test

example_should_not_equal = Examples.some_tpe . should_not_equal_tpe Vector.Vector
Any.should_not_equal_type : Any -> Integer -> Assertion
Any.should_not_equal_type self that frames_to_skip=0 = case (self.is_same_object_as that . not) of
True -> Success
False ->
loc = Meta.get_source_location 2+frames_to_skip
msg = self.to_text + " did equal type " + that.to_text + " (at " + loc + ")."
fail msg

## Asserts that `self` value is equal to the expected value.

Arguments:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package org.enso.interpreter.node.controlflow.caseexpr;

import com.oracle.truffle.api.RootCallTarget;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.nodes.NodeInfo;
import com.oracle.truffle.api.profiles.ConditionProfile;
import org.enso.interpreter.runtime.data.Type;
import org.enso.interpreter.runtime.data.EnsoDate;

/** An implementation of the case expression specialised to working on Date. */
@NodeInfo(shortName = "DateMatch")
public abstract class DateBranchNode extends BranchNode {
private final Type date;
private final ConditionProfile profile = ConditionProfile.createCountingProfile();

DateBranchNode(Type vector, RootCallTarget branch) {
super(branch);
this.date = vector;
}

/**
* Creates a new node for handling matching on a case expression.
*
* @param date the expression to use for matching
* @param branch the expression to be executed if (@code matcher} matches
* @return a node for matching in a case expression
*/
public static DateBranchNode build(Type date, RootCallTarget branch) {
return DateBranchNodeGen.create(date, branch);
}

@Specialization
void doType(VirtualFrame frame, Object state, Type target) {
if (profile.profile(date == target)) {
accept(frame, state, new Object[0]);
}
}

@Specialization
void doEnsoDate(VirtualFrame frame, Object state, EnsoDate date) {
accept(frame, state, new Object[0]);
}

@Specialization(guards = {"interop.isDate(date)", "!interop.isTime(date)"})
void doDate(
VirtualFrame frame,
Object state,
Object date,
@CachedLibrary(limit = "10") InteropLibrary interop) {
accept(frame, state, new Object[0]);
}

@Fallback
void doFallback(VirtualFrame frame, Object state, Object target) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package org.enso.interpreter.node.controlflow.caseexpr;

import com.oracle.truffle.api.RootCallTarget;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.nodes.NodeInfo;
import com.oracle.truffle.api.profiles.ConditionProfile;
import org.enso.interpreter.runtime.data.EnsoDateTime;
import org.enso.interpreter.runtime.data.Type;

/** An implementation of the case expression specialised to working on Date_Time. */
@NodeInfo(shortName = "DateTimeMatch")
public abstract class DateTimeBranchNode extends BranchNode {
private final Type dateTime;
private final ConditionProfile profile = ConditionProfile.createCountingProfile();

DateTimeBranchNode(Type vector, RootCallTarget branch) {
super(branch);
this.dateTime = vector;
}

/**
* Creates a new node for handling matching on a case expression.
*
* @param dateTime the expression to use for matching
* @param branch the expression to be executed if (@code matcher} matches
* @return a node for matching in a case expression
*/
public static DateTimeBranchNode build(Type dateTime, RootCallTarget branch) {
return DateTimeBranchNodeGen.create(dateTime, branch);
}

@Specialization
void doType(VirtualFrame frame, Object state, Type target) {
if (profile.profile(dateTime == target)) {
accept(frame, state, new Object[0]);
}
}

@Specialization
void doEnsoDateTime(VirtualFrame frame, Object state, EnsoDateTime dateTime) {
accept(frame, state, new Object[0]);
}

@Specialization(
guards = {
"interop.isDate(dateTime)",
"interop.isTime(dateTime)",
})
void doDateTime(
VirtualFrame frame,
Object state,
Object dateTime,
@CachedLibrary(limit = "10") InteropLibrary interop) {
accept(frame, state, new Object[0]);
}

@Fallback
void doFallback(VirtualFrame frame, Object state, Object target) {}
}
Loading

0 comments on commit 7a6ee0c

Please sign in to comment.