MattWindsor91 / roslyn-concepts-issues

Issue-tracking repository for captainhayashi/roslyn
4 stars 0 forks source link

Concept constraint syntax #1

Open MattWindsor91 opened 8 years ago

MattWindsor91 commented 8 years ago

In a recent meeting it was decided that the current overloading of the C# where syntax is too confusing: it causes where to mean two only-slightly-related concepts (pun unintended). We need to create a new syntax, but it's unclear which would be better.

Existing

A concept witness is introduced into a method or class as follows:

A Sum<A>(A[] xs) where NumA : Num<A> { /* ... */ }

Note that NumA does not appear in the type parameter list. This is the clue that makes the compiler realise this is a concept witness, and translate it to

A Sum<A, [ConceptWitness] NumA>(A[] xs) where NumA : struct, Num<A> { /* ... */ }

The fact that the semantics depends on whether NumA is in the type parameters or not is quite surprising!

Proposal 1: No syntax

Do not add any syntactic sugar: programmers must write

A Sum<A, [ConceptWitness] NumA>(A[] xs) where NumA : struct, Num<A> { /* ... */ }

Pros

Use the same sort of syntax as where, but with a different keyword. May need to engineer the correct keyword or keyword chord to use.

Some possibilities I like:

A Sum<A>(A[] xs) instance NumA : Num<A> { /* ... */ }
A Sum<A>(A[] xs) concept NumA : Num<A> { /* ... */ }
A Sum<A>(A[] xs) where instance NumA : Num<A> { /* ... */ }
A Sum<A>(A[] xs) where concept NumA : Num<A> { /* ... */ }
A Sum<A>(A[] xs) where implicit NumA : Num<A> { /* ... */ }
A Sum<A>(A[] xs) implicit instance NumA : Num<A> { /* ... */ }
A Sum<A>(A[] xs) implicit concept NumA : Num<A> { /* ... */ }
// ...

Pros

Add some sort of syntactic modifier to type parameters to pick them out as concept witnesses.

A Sum<A, NumA : Num<A>>(A[] xs) { /* ... */ }
A Sum<A, NumA = Num<A>>(A[] xs) { /* ... */ }
A Sum<A, instance NumA : Num<A>>(A[] xs) { /* ... */ }
A Sum<A, instance NumA = Num<A>>(A[] xs) { /* ... */ }
// ...

Pros

Add syntax similar to Haskell.

NumA : Num<A> => A Sum<A>(A[] xs) { /* ... */ }

Pros

Personally, I'm in favour of proposal 1 (mainly from an implementation point of view), but as I'm not used to the concerns of C# syntax design I'm not entirely sure this is the best way forwards.

Additional proposals and comments welcome.

MattWindsor91 commented 8 years ago

A possible extension of proposal 2 is to allow syntax for pulling existing concept instances (either defined in methods/classes other than the current one but in scope, or ground instances) into the current scope as if they were witnessed by an implicit type parameter, eg:

void Foo<A>(A a, int I)
    implicit instance BarA : Bar<A> // normal witness, becomes a type parameter
    using instance BarInt // finds instance BarInt in scope and makes it available for resolution, no new type parameters
{
  // ...
}

Similarly, associated types could be given the pseudo-where treatment:

concept Set
    implicit type Element // becomes new type parameter <[AssociatedType] Element>
{
  // ...
}

instance SetInt : Set
   implicit type Element = int // does unknown magic to satisfy type parameter
{
  // ...
}

The syntax here is just a sketch, though.