Skip to content
50 changes: 50 additions & 0 deletions docs/types/intersection-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 -> ...
Copy link
Contributor

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?

Suggested change
a : A -> ...
b : B -> ...
a : A -> ...

Copy link
Member

Choose a reason for hiding this comment

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

case of patterns do not invoke conversions

_ -> ...
```
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`
Copy link
Contributor

Choose a reason for hiding this comment

The 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? x.method_defined_on_B?

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
Copy link
Contributor

Choose a reason for hiding this comment

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

What happens if there are conversions from both A and B to some other type? Do unhidden ones take precedence over hidden ones?

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:
Expand Down Expand Up @@ -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
Copy link
Member

Choose a reason for hiding this comment

The 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?

self preserves intersection types, just hides them

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)
Loading