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

By-type pattern matching #3742

Merged
merged 18 commits into from
Oct 4, 2022
Merged

By-type pattern matching #3742

merged 18 commits into from
Oct 4, 2022

Conversation

hubertp
Copy link
Contributor

@hubertp hubertp commented Sep 27, 2022

Pull Request Description

This change brings by-type pattern matching to Enso.
One can pattern match on Enso types as well as on polyglot types.

For example,

case x of
    _ : Integer -> ...
    _ : Text -> ...
   _ -> ...

as well as Java's types

case y of 
    _ : ArrayList -> ...
    _ : List -> ...
    _ : AbstractList -> ...
    _ -> ..

It is no longer possible to match a value with a corresponding type constructor.
For example

case Date.now of
    Date -> ...

will no longer match and one should match on the type (_ : Date) instead.

case Date of
    Date -> ...

is fine though, as requested in the ticket.

The change required further changes to type_of logic which wasn't dealing well with polyglot values.

Implements https://www.pivotaltracker.com/story/show/183188846

Important Notes

I discovered late in the game that nested patterns involving type patterns, such as Const (f : Foo) tail -> ... are not possible due to the old parser logic.
I would prefer to add it in a separate PR because this one is already getting quite large.
This is now supported!

Checklist

Please include the following checklist in your PR:

  • The documentation has been updated if necessary.
  • All code conforms to the
    Scala,
    Java,
    and
    Rust
    style guides.
  • All code has been tested:
    • Unit tests have been written where possible.
    • If GUI codebase was changed: Enso GUI was tested when built using BOTH
      ./run ide build and ./run ide watch.

@hubertp hubertp force-pushed the wip/hubert/type-pattern-183188846 branch from 6bb58fe to cafbb2e Compare September 30, 2022 08:32
@hubertp hubertp marked this pull request as ready for review September 30, 2022 10:23
boolean test = isSameObject.execute(expectedType, typeOfTarget);
if (profile.profile(test)) {
accept(frame, state, new Object[] {target});
} else if (TypesGen.isType(typeOfTarget)) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@JaroslavTulach I suppose I should profile conditions in this branch as well?

Copy link
Member

Choose a reason for hiding this comment

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

Can we analyze the positive/negative check? Check the IGV graphs or have a benchmark for the simple expectedType equals typeOfTarget and for the case when it is not? Especially when there are some tpe.getSupertype()?

If we can guarantee some stability of superTpe, then this while cycle would be candidate for @ExplodeLoop...

CHANGELOG.md Outdated Show resolved Hide resolved
Copy link
Member

@jdunkerley jdunkerley left a comment

Choose a reason for hiding this comment

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

couple of minor bits - only looked at the enso stuff.

distribution/lib/Standard/Base/0.0.0-dev/src/Meta.enso Outdated Show resolved Hide resolved
test/Tests/src/Semantic/Case_Spec.enso Outdated Show resolved Hide resolved
Copy link
Member

@radeusgd radeusgd left a comment

Choose a reason for hiding this comment

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

This is awesome! I'm glad we finally have that.

Looks good, just a few comments/questions in-line.

Text -> if other == "" then self else Builder_Data (self.fragments ++ (code other).fragments)
text : Text -> if text == "" then self else Builder_Data (self.fragments ++ (code text).fragments)
Copy link
Member

Choose a reason for hiding this comment

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

Wow, I didn't know we'll be able to do that too! It's great!

@@ -25,4 +25,6 @@ spec =
pattern = "http://example.com"
Regex.escape pattern . should_equal "\Qhttp://example.com\E"

## TODO: Missing tests for No_Such_Group_Error
Copy link
Member

Choose a reason for hiding this comment

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

Any more information on what happened here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is more of a wishlist since No_Such_Group_Error.to_display_text pattern matches on Text and Integer types but I only discovered those via a simple grep because all tests were passing.

test/Tests/src/Semantic/Case_Spec.enso Outdated Show resolved Hide resolved
test/Tests/src/Semantic/Case_Spec.enso Outdated Show resolved Hide resolved
@hubertp
Copy link
Contributor Author

hubertp commented Sep 30, 2022

Added supported for deeply nested type patterns, such as Cons (num : Integer) Nil -> ...

Copy link
Member

@JaroslavTulach JaroslavTulach left a comment

Choose a reason for hiding this comment

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

Without no doubts this change has to get in! I have some doubts when Node references a value, but that is a strategic problem, not something that has to be resolved in this PR. Thank you @hubertp for simplifying the logic of branching by reducing the number of various branch nodes!

@NodeInfo(shortName = "PolyglotSymbolTypeMatch")
public abstract class CatchPolyglotSymbolTypeBranchNode extends BranchNode {

private final Object polyglotSymbol;
Copy link
Member

Choose a reason for hiding this comment

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

Code (e.g. Node subclasses) cannot directly hold on values, if we ever want to use single Engine for multiple Contexts. CCing @kustosz

Enso symbols aren't referenced directly, but via Enso Context and that's OK. We need something same for non-Enso values.

CatchTypeBranchNode(Type tpe, RootCallTarget functionNode) {
super(functionNode);
this.expectedType = tpe;
this.isArrayExepctedType = Context.get(this).getBuiltins().array() == tpe;
Copy link
Member

Choose a reason for hiding this comment

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

There is (or can be) 1:N mapping between Node and Context instances. Storing tpe in Node prevents us to use multiple Contexts with a single Engine.

boolean test = isSameObject.execute(expectedType, typeOfTarget);
if (profile.profile(test)) {
accept(frame, state, new Object[] {target});
} else if (TypesGen.isType(typeOfTarget)) {
Copy link
Member

Choose a reason for hiding this comment

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

Can we analyze the positive/negative check? Check the IGV graphs or have a benchmark for the simple expectedType equals typeOfTarget and for the case when it is not? Especially when there are some tpe.getSupertype()?

If we can guarantee some stability of superTpe, then this while cycle would be candidate for @ExplodeLoop...

@@ -1,63 +0,0 @@
package org.enso.interpreter.node.controlflow.caseexpr;
Copy link
Member

Choose a reason for hiding this comment

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

Great to see all these special branch nodes being deleted!

test/Tests/src/Semantic/Case_Spec.enso Show resolved Hide resolved
test/Tests/src/Semantic/Case_Spec.enso Show resolved Hide resolved
@@ -829,6 +829,17 @@ object AstView {
case Pattern(pat) => Some((pat, right))
case _ => None
}
case AST.App.Infix(
Copy link
Member

Choose a reason for hiding this comment

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

CCing @kazcw.

@@ -968,52 +968,9 @@ class IrToTruffle(
) =>
val tpe =
mod.unsafeAsModule().getScope.getTypes.get(tp.name)
val any = context.getBuiltins.any
Copy link
Member

Choose a reason for hiding this comment

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

Removing all the specialized branch nodes and replacing them with a single construct is a magnificent improvement regardless of any other comments.

@hubertp hubertp force-pushed the wip/hubert/type-pattern-183188846 branch 2 times, most recently from 4348b1f to 52f274c Compare October 3, 2022 14:20
hubertp and others added 16 commits October 4, 2022 09:30
`scope.getType` vs `scope.getTypes.get` is easy to get wrong.
Now
```
case Date_Time.now of
  _ : Date_Time.Date_Time -> "OK"
  _ ->
```
works as expected.
We can now pattern match on types!
Polyglot values coming from JS or Python were problematic because they
followed a different path than Java's polyglot values.
The type of JS' Array values was PolyglotProxy which we need to
specialize on.
Python's Array values are on the other hand represented as Python list.
Since it is impossible to special case those when inferring the type,
CatchType has a specialization on Array-like values that have a meta
object but no type.

Additionally added support for pattern matching on polyglot symbols
(both in types and as values).
And replace it with pattern matching on Text type.
Also now supporting subtyping for Enso as well as polyglot types.
Co-authored-by: Radosław Waśko <radoslaw.wasko@enso.org>
This messes up aliasing info, which in turn affects global names pass.
@hubertp hubertp force-pushed the wip/hubert/type-pattern-183188846 branch from 1d55a67 to cb7e952 Compare October 4, 2022 07:30
@hubertp hubertp added the CI: Ready to merge This PR is eligible for automatic merge label Oct 4, 2022
@mergify mergify bot merged commit ae66087 into develop Oct 4, 2022
@mergify mergify bot deleted the wip/hubert/type-pattern-183188846 branch October 4, 2022 09:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CI: Ready to merge This PR is eligible for automatic merge
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants