Skip to content

Latest commit

 

History

History
206 lines (150 loc) · 6.01 KB

p0553.md

File metadata and controls

206 lines (150 loc) · 6.01 KB

Generics details part 1

Pull request

Table of contents

Problem

We want to Carbon to have a high quality generics feature that achieves the goals set out in #24. This is too big to land in a single proposal. This proposal goes into the details of the core of the feature, and provides an outline covering future work. It covers:

  • interfaces
  • implementing interfaces for types
  • resolving name conflicts
  • facet types
  • type-types as the way of describing type variables
  • structural interfaces
  • combining interfaces
  • interface requirements and extension
  • type compatibility

Background

This is a follow on to these previous generics proposals:

The content for this proposal was extracted from a larger Generics combined draft proposal.

Proposal

This is a proposal to add this detailed design document.

Rationale based on Carbon's goals

Much of this rationale was captured in the Generics goals proposal.

Alternatives considered

Interface implementation syntax

The interface implementation syntax was decided in question-for-leads issue #575.

struct Song {
  // data and methods ...
  impl as Printable {
    method (me: Self) Print() { ... }
  }
}
external impl Song as Comparable { ... }

This proposal includes additional discussion and additional alternatives.

for instead of as in external impl

In this option, the interface name comes before the type name.

struct Song { ... }
external impl Comparable for Song { ... }

Advantage:

  • This ordering used by Rust.

Disadvantages:

  • We prefer the type name before the interface name (using as), since having the type first and outer is consistent with those implemented in struct declarations. It also seems more natural to express the parameters to the interface in terms of the parameters and associated items of the type than the other way around.
  • The Song as Comparable phrase is the name of the facet type that is being implemented.

No as for inline impl

struct Song {
  // data and methods ...
  impl Printable {
    method (me: Self) Print() { ... }
  }
}

Advantage:

  • More concise, so less to read and write.

Disadvantage:

  • Less consistent with the external impl syntax.
  • Less consistent with the planned inline conditional impl syntax.

No external for external impl

struct Song { ... }
impl Song as Comparable { ... }

Advantage:

  • More concise, so less to read and write.

Disadvantages:

  • Less explicit that the methods of this impl definition are not contributing to unqualified API of the type.
  • This kind of implementation is naturally referred to as "external", especially when contrasting with "inline impl".

Out-of-line impl

We considered an out-of-line syntax for declaring and defining interface impl blocks, to be consistent with the external impl declarations. For example:

struct Song { ... }
impl Printable for Song { ... }
external impl Comparable for Song { ... }

The main advantage of this syntax was that it was uniform across many cases, including conditional conformance. It wasn't ideal across a number of dimensions though.

  • It repeated the type name which was redundant and verbose
  • It could affect the API of the type outside of the type definition.

extend blocks

Instead of the external impl statement, we considered putting all external implementations in an expand block.

struct Song {
  impl Printable { ... }
}
expand Song {
  impl Comparable { ... }
}

Advantages:

  • This option is most similar to the approach used by Swift.
  • Easier to copy-paste an impl between a struct definition and an expand block.

The expand approach had some disadvantages:

  • Implementations were indented more than the external impl approach.
  • Extra ceremony in the case of only implementing one type for an interface. This case is expected to be common since external implementations will most often be defined with the interface.
  • When implementing multiple interfaces in a single expand block, the name of the type being expanded could be far from the impl declaration and hard to find.

We originally used extend instead of expand but that collided with using extends for interface extension and derived classes.

Others

Other alternatives considered will be in a future proposal. Some of them can be seen in a rough form in #36.