-
Notifications
You must be signed in to change notification settings - Fork 334
Documenting expected behaviour of intersection types #13384
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
base: develop
Are you sure you want to change the base?
Changes from all commits
63a0a6e
ca97fbd
12c6c28
4ed5160
10e83f5
729226d
d282a73
e4db4c6
57aacfe
11525c1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -209,6 +209,49 @@ In short: when a [conversion](../syntax/conversions.md) is needed to satisfy a | |
| type check a new value is created to satisfy just the types requested in the | ||
| check. | ||
|
|
||
| ## Preserving identity of refined types | ||
|
|
||
| Intersection types are often used to refine a value with additional | ||
| functionality. For example, a value `table : Table & DB_Table` represents a | ||
| table coming from a database that has both the base `Table` methods that all | ||
| tables share but also database-specific methods from `DB_Table`. Either part of | ||
| this compound type may get _hidden_ when passing around various methods (e.g. | ||
| `table:Table` will hide the `DB_Table` part), but the value itself still retains | ||
| its identity as a `Table & DB_Table` and can be uncovered via a cast or a case | ||
| expression during runtime. | ||
|
|
||
| To ensure intersection types properly propagate thru the Enso program the | ||
| basic language constructs are designed to handle them properly. Namely: | ||
|
|
||
| - casting `x : A` will hide the `B` part, so the actual type is | ||
| `x : A & (hidden B)`. | ||
| - inspecting the type via `case of`, e.g. | ||
| ``` | ||
| case x of | ||
| a : A -> ... | ||
| _ -> ... | ||
| ``` | ||
| in the first branch, the `a` has visible type `A` but the full type is still | ||
| `a : A & ~B` - the `B` part is hidden, but not removed. | ||
| - dispatching a method on one of the types, e.g. calling `x.method_defined_on_A` | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it the same when calling a method defined on a hidden type? |
||
| will pass the value `x` as `self` to the body of `method_defined_on_A`, the | ||
| `self` will have a visible type `A`, but the full type is still | ||
| `self : A & ~B` - so the `self` value can be cast if needed. | ||
| - normally, calling conversions will 'start over' and the value returned from | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What happens if there are conversions from both |
||
| one will not have an intersection type anymore. However, as long as possible, | ||
| even conversions should try to use existing (even if hidden) parts of the type | ||
| instead of calling into the conversion code; thus: | ||
| - calling `A.from x` will return a value of type `A & ~B` - the `B` | ||
| part is still there (only hidden), because no actual conversion code had to | ||
| be run - the type was simply extracted from the intersection type. | ||
| - similarly, calling `B.from x` will return a value of type `B & ~A` - | ||
| the `A` part is still there (only hidden), because no actual conversion code | ||
| had to be run. | ||
| It is important to ensure these operations do not remove a part of the intersection type. | ||
| Otherwise the value loses part of its functionality which would have a detrimental effect | ||
| in the GUI, confusing users. | ||
| If the `DB_Table` part is not kept as hidden, but completely removed, the | ||
| table can no longer be casted to `DB_Table` and used as such. | ||
| ## Signature vs. Cast | ||
|
|
||
| There are two slightly different places where _type checking_ occurs: | ||
|
|
@@ -299,3 +342,10 @@ _has been cast to_. As a special case any value wrapped into an _intersection | |
| type_, but _cast down_ to the original type is `==` and has the same `hash` as | ||
| the original value. E.g. `4.2 : Complex&Float : Float` is `==` and has the same | ||
| `hash` as `4.2` (in spite it _can be cast to_ `Complex`). | ||
|
|
||
| ### TODO | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's the purpose of this TODO? If the functionality is broken, can we link to the appropriate issues?
That works, doesn't it? |
||
|
|
||
| - self preserves intersection types, just hides them | ||
| - case of also | ||
| - T.from should preserve if no conversion was made (e.g. T was among visible or | ||
| hidden types) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens in this case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
case ofpatterns do not invoke conversions