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

Facet relationship between parameterized types #900

Merged
merged 1 commit into from
Oct 19, 2021
Merged
Changes from all commits
Commits
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
44 changes: 30 additions & 14 deletions docs/design/generics/details.md
Original file line number Diff line number Diff line change
Expand Up @@ -1398,23 +1398,34 @@ Now consider a type with a generic type parameter, like a hash map type:

```
interface Hashable { ... }
class HashMap(KeyT:! Hashable, ValueT:! Type) { ... }
class HashMap(KeyT:! Hashable, ValueT:! Type) {
fn Find[me:Self](key: KeyT) -> Optional(ValueT);
// ...
}
```

If we write something like `HashMap(String, i32)` the type we actually get is:
A user of this type will provide specific values for the key and value types:

```
HashMap(String as Hashable, i32 as Type)
var hm: HashMap(String, i32) = ...;
var result: Optional(i32) = hm.Find("Needle");
```

This is the same type we will get if we pass in some other facet types in, so
all of these types are equal:
Since the `Find` function is generic, it can only use the capabilities that
`HashMap` requires of `KeyT` and `ValueT`. This implies that the
_implementation_ of `HashMap(String, i32).Find` and
`HashMap(String as Hashable, i32).Find` are the same. In fact, we could
substitute any facet of `String`, and `Find` would still use
`String as Hashable` in its implementation. So these types:

- `HashMap(String, i32)`
- `HashMap(String as Hashable, i32 as Type)`
- `HashMap((String as Printable) as Hashable, i32)`
- `HashMap(String as Printable, i32)`
- `HashMap((String as Printable & Hashable) as Hashable, i32)`

are also facets of each other, and Carbon can freely allow casts and implicit
conversions between them.

This means we don't generally need to worry about getting the wrong facet type
as the argument for a generic type. This means we don't get type mismatches when
calling functions as in this example, where the type parameters have different
Expand All @@ -1425,10 +1436,16 @@ fn PrintValue
[KeyT:! Printable & Hashable, ValueT:! Printable]
(map: HashMap(KeyT, ValueT), key: KeyT) { ... }

var m: HashMap(String, i32);
var m: HashMap(String, i32) = ...;
PrintValue(m, "key");
```

However, those types are still different. A caller of `Find` observes that its
signature reflects the actual type parameters passed to `HashMap`, not their
projection onto the `Hashable` or `Type` facets. In particular, the return type
of `hm.Find` is `Optional(i32)`, not `Optional(i32 as Type)`. (Incidentally,
`Optional(i32)` and `Optional(i32 as Type)` are also facets of each other.)

## Adapting types

Since interfaces may only be implemented for a type once, and we limit where
Expand Down Expand Up @@ -1574,13 +1591,12 @@ one difference between them is that `Song as Hashable` may be implicitly
converted to `Song`, which implements interface `Printable`, and
`PlayableSong as Hashable` may be implicilty converted to `PlayableSong`, which
implements interface `Media`. This means that it is safe to convert between
`HashMap(Song, i32) == HashMap(Song as Hashable, i32)` and
`HashMap(PlayableSong, i32) == HashMap(PlayableSong as Hashable, i32)` (though
maybe only with an explicit cast) but
`HashMap(SongHashedByTitle, i32) == HashMap(SongHashByTitle as Hashable, i32)`
is incompatible. This is a relief, because we know that in practice the
invariants of a `HashMap` implementation rely on the hashing function staying
the same.
`HashMap(Song, i32)` and `HashMap(PlayableSong, i32)` (though maybe only with an
explicit cast), since the implementation of all the methods will use the same
implementation of the `Hashable` interface. But
`HashMap(SongHashedByTitle, i32)` is incompatible. This is a relief, because we
know that in practice the invariants of a `HashMap` implementation rely on the
hashing function staying the same.

### Extending adapter

Expand Down