Skip to content

Trial balloon for fixes to given syntax #7150

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

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
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
34 changes: 18 additions & 16 deletions docs/docs/internals/syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,8 +232,8 @@ Quoted ::= ‘'’ ‘{’ Block ‘}’
ExprsInParens ::= ExprInParens {‘,’ ExprInParens}
ExprInParens ::= PostfixExpr ‘:’ Type -- normal Expr allows only RefinedType here
| Expr
ParArgumentExprs ::= ‘(’ ExprsInParens ‘)’ exprs
| ‘(’ [ExprsInParens ‘,’] PostfixExpr ‘:’ ‘_’ ‘*’ ‘)’ exprs :+ Typed(expr, Ident(wildcardStar))
ParArgumentExprs ::= ‘(’ [‘given’] ExprsInParens ‘)’ exprs
| ‘(’ [ExprsInParens ‘,’] PostfixExpr ‘:’ ‘_’ ‘*’ ‘)’ exprs :+ Typed(expr, Ident(wildcardStar))
ArgumentExprs ::= ParArgumentExprs
| [cnl] BlockExpr
BlockExpr ::= ‘{’ CaseClauses | Block ‘}’
Expand Down Expand Up @@ -298,7 +298,7 @@ HkTypeParam ::= {Annotation} [‘+’ | ‘-’] (Id[HkTypeParamClause] |
ClsParamClauses ::= {ClsParamClause} [[nl] ‘(’ [‘implicit’] ClsParams ‘)’]
| {ClsParamClause} {GivenClsParamClause}
ClsParamClause ::= [nl] ‘(’ ClsParams ‘)’
GivenClsParamClause::= ‘given’ (‘(’ ClsParams ‘)’ | GivenTypes)
GivenClsParamClause::= ‘(’ ‘given’ (ClsParams | GivenTypes) ‘)’
ClsParams ::= ClsParam {‘,’ ClsParam}
ClsParam ::= {Annotation} ValDef(mods, id, tpe, expr) -- point of mods on val/var
[{Modifier} (‘val’ | ‘var’) | ‘inline’] Param
Expand All @@ -308,7 +308,7 @@ Param ::= id ‘:’ ParamType [‘=’ Expr]
DefParamClauses ::= {DefParamClause} [[nl] ‘(’ [‘implicit’] DefParams ‘)’]
| {DefParamClause} {GivenParamClause}
DefParamClause ::= [nl] ‘(’ DefParams ‘)’
GivenParamClause ::= ‘given’ (‘(’ DefParams ‘)’ | GivenTypes)
GivenParamClause ::= ‘(’ ‘given’ (DefParams | GivenTypes) ‘)’
DefParams ::= DefParam {‘,’ DefParam}
DefParam ::= {Annotation} [‘inline’] Param ValDef(mods, id, tpe, expr) -- point of mods at id.
GivenTypes ::= AnnotType {‘,’ AnnotType}
Expand All @@ -335,12 +335,16 @@ AccessQualifier ::= ‘[’ (id | ‘this’) ‘]’

Annotation ::= ‘@’ SimpleType {ParArgumentExprs} Apply(tpe, args)

Import ::= ‘import’ [‘given’] ImportExpr {‘,’ ImportExpr}
ImportExpr ::= StableId ‘.’ (id | ‘_’ | ImportSelectors) Import(expr, sels)
ImportSelectors ::= ‘{’ {ImportSelector ‘,’} FinalSelector ‘}’
FinalSelector ::= ImportSelector Ident(name)
| ‘_’ [‘:’ Type] TypeBoundsTree(EmptyTree, tpt)
ImportSelector ::= id [‘=>’ id | ‘=>’ ‘_’]
Import ::= ‘import’ ImportExpr {‘,’ ImportExpr}
ImportExpr ::= StableId ‘.’ ImportSpec Import(expr, sels)
ImportSpec ::= id
| ‘_’
| ‘given’
| ‘{’ ImportSelectors) ‘}’
ImportSelectors ::= [‘given’] id [‘=>’ id | ‘=>’ ‘_’] [‘,’ ImportSelectors]
| WildCardSelector {‘,’ WildCardSelector}
WildCardSelector ::= ‘given’ [‘as’ InfixType]
| ‘_' [‘:’ InfixType]
Export ::= ‘export’ [‘given’] ImportExpr {‘,’ ImportExpr}
```

Expand Down Expand Up @@ -382,17 +386,15 @@ ClassConstr ::= [ClsTypeParamClause] [ConstrMods] ClsParamClauses
ConstrMods ::= {Annotation} [AccessModifier]
ObjectDef ::= id [Template] ModuleDef(mods, name, template) // no constructor
EnumDef ::= id ClassConstr InheritClauses EnumBody EnumDef(mods, name, tparams, template)
GivenDef ::= [id] [DefTypeParamClause] GivenBody
GivenBody ::= [‘as’ ConstrApp {‘,’ ConstrApp }] {GivenParamClause} [TemplateBody]
| ‘as’ Type {GivenParamClause} ‘=’ Expr
GivenDef ::= [id] [DefTypeParamClause] {GivenParamClause} GivenBody
GivenBody ::= [‘as’ ConstrApp {‘,’ ConstrApp }] [TemplateBody]
| ‘as’ Type ‘=’ Expr
| ‘(’ DefParam ‘)’ TemplateBody
Template ::= InheritClauses [TemplateBody] Template(constr, parents, self, stats)
InheritClauses ::= [‘extends’ ConstrApps] [‘derives’ QualId {‘,’ QualId}]
ConstrApps ::= ConstrApp {‘with’ ConstrApp}
| ConstrApp {‘,’ ConstrApp}
ConstrApp ::= SimpleConstrApp
| ‘(’ SimpleConstrApp {‘given’ (PrefixExpr | ParArgumentExprs)} ‘)’
SimpleConstrApp ::= AnnotType {ArgumentExprs} Apply(tp, args)
ConstrApp ::= AnnotType {ArgumentExprs} Apply(tp, args)
ConstrExpr ::= SelfInvocation
| ‘{’ SelfInvocation {semi BlockStat} ‘}’
SelfInvocation ::= ‘this’ ArgumentExprs {ArgumentExprs}
Expand Down
6 changes: 3 additions & 3 deletions docs/docs/reference/contextual/context-bounds.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ A context bound is a shorthand for expressing the common pattern of an implicit
```scala
def maximum[T: Ord](xs: List[T]): T = xs.reduceLeft(max)
```
A bound like `: Ord` on a type parameter `T` of a method or class indicates an implicit parameter `given Ord[T]`. The implicit parameter(s) generated from context bounds come last in the definition of the containing method or class. E.g.,
A bound like `: Ord` on a type parameter `T` of a method or class indicates an implicit parameter `(given Ord[T])`. The implicit parameter(s) generated from context bounds come last in the definition of the containing method or class. E.g.,
```scala
def f[T: C1 : C2, U: C3](x: T) given (y: U, z: V): R
def f[T: C1 : C2, U: C3](x: T)(given y: U, z: V): R
```
would expand to
```scala
def f[T, U](x: T) given (y: U, z: V) given C1[T], C2[T], C3[U]: R
def f[T, U](x: T)(given y: U, z: V)(given C1[T], C2[T], C3[U]): R
```
Context bounds can be combined with subtype bounds. If both are present, subtype bounds come first, e.g.
```scala
Expand Down
12 changes: 6 additions & 6 deletions docs/docs/reference/contextual/conversions.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ abstract class Conversion[-T, +U] extends (T => U)
```
For example, here is an implicit conversion from `String` to `Token`:
```scala
given as Conversion[String, Token] {
given Conversion[String, Token] {
def apply(str: String): Token = new KeyWord(str)
}
```
Using an alias this can be expressed more concisely as:
```scala
given as Conversion[String, Token] = new KeyWord(_)
given Conversion[String, Token] = new KeyWord(_)
```
An implicit conversion is applied automatically by the compiler in three situations:

Expand All @@ -37,7 +37,7 @@ If such an instance `C` is given, the expression `e` is replaced by `C.apply(e)`
primitive number types to subclasses of `java.lang.Number`. For instance, the
conversion from `Int` to `java.lang.Integer` can be defined as follows:
```scala
given int2Integer as Conversion[Int, java.lang.Integer] =
given int2Integer: Conversion[Int, java.lang.Integer] =
java.lang.Integer.valueOf(_)
```

Expand All @@ -59,9 +59,9 @@ object Completions {
//
// CompletionArg.fromStatusCode(statusCode)

given fromString as Conversion[String, CompletionArg] = Error(_)
given fromFuture as Conversion[Future[HttpResponse], CompletionArg] = Response(_)
given fromStatusCode as Conversion[Future[StatusCode], CompletionArg] = Status(_)
given fromString : Conversion[String, CompletionArg] = Error(_)
given fromFuture : Conversion[Future[HttpResponse], CompletionArg] = Response(_)
given fromStatusCode : Conversion[Future[StatusCode], CompletionArg] = Status(_)
}
import CompletionArg._

Expand Down
36 changes: 17 additions & 19 deletions docs/docs/reference/contextual/delegates.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ trait Ord[T] {
def (x: T) > (y: T) = compare(x, y) > 0
}

given IntOrd as Ord[Int] {
given intOrd: Ord[Int] {
def compare(x: Int, y: Int) =
if (x < y) -1 else if (x > y) +1 else 0
}

given ListOrd[T] as Ord[List[T]] given (ord: Ord[T]) {
given listOrd[T](given ord: Ord[T]): Ord[List[T]] {

def compare(xs: List[T], ys: List[T]): Int = (xs, ys) match {
case (Nil, Nil) => 0
Expand All @@ -30,19 +30,19 @@ given ListOrd[T] as Ord[List[T]] given (ord: Ord[T]) {
}
}
```
This code defines a trait `Ord` with two given instances. `IntOrd` defines
a given for the type `Ord[Int]` whereas `ListOrd[T]` defines givens
This code defines a trait `Ord` with two given instances. `intOrd` defines
a given for the type `Ord[Int]` whereas `listOrd[T]` defines givens
for `Ord[List[T]]` for all types `T` that come with a given instance for `Ord[T]` themselves.
The `given (ord: Ord[T])` clause in `ListOrd` defines an implicit parameter.
The `(given ord: Ord[T])` clause in `listOrd` defines an implicit parameter.
Given clauses are further explained in the [next section](./given-clauses.md).

## Anonymous Given Instances

The name of a given instance can be left out. So the definitions
of the last section can also be expressed like this:
```scala
given as Ord[Int] { ... }
given [T] as Ord[List[T]] given Ord[T] { ... }
given Ord[Int] { ... }
given [T](given Ord[T]): Ord[List[T]] { ... }
```
If the name of a given is missing, the compiler will synthesize a name from
the type(s) in the `as` clause.
Expand All @@ -51,7 +51,7 @@ the type(s) in the `as` clause.

An alias can be used to define a given instance that is equal to some expression. E.g.:
```scala
given global as ExecutionContext = new ForkJoinPool()
given global: ExecutionContext = new ForkJoinPool()
```
This creates a given `global` of type `ExecutionContext` that resolves to the right
hand side `new ForkJoinPool()`.
Expand All @@ -60,8 +60,8 @@ returned for this and all subsequent accesses to `global`.

Alias givens can be anonymous, e.g.
```scala
given as Position = enclosingTree.position
given as Context given (outer: Context) = outer.withOwner(currentOwner)
given Position = enclosingTree.position
given (given outer: Context): Context = outer.withOwner(currentOwner)
```
An alias given can have type parameters and given clauses just like any other given instance, but it can only implement a single type.

Expand All @@ -78,14 +78,12 @@ Here is the new syntax of given instances, seen as a delta from the [standard co
```
TmplDef ::= ...
| ‘given’ GivenDef
GivenDef ::= [id] [DefTypeParamClause] GivenBody
GivenBody ::= [‘as’ ConstrApp {‘,’ ConstrApp }] {GivenParamClause} [TemplateBody]
| ‘as’ Type {GivenParamClause} ‘=’ Expr
ConstrApp ::= SimpleConstrApp
| ‘(’ SimpleConstrApp {‘given’ (PrefixExpr | ParArgumentExprs)} ‘)’
SimpleConstrApp ::= AnnotType {ArgumentExprs}
GivenParamClause ::= ‘given’ (‘(’ [DefParams] ‘)’ | GivenTypes)
GivenDef ::= GivenBody
| [id] [DefTypeParamClause] {GivenParamClause}
(‘:’ GivenBody | ‘<:’ Type ‘=’ Expr)
GivenBody ::= [ConstrApp {‘,’ ConstrApp }] [TemplateBody]
| Type ‘=’ Expr
GivenParamClause ::= ‘(’ ‘given’ (DefParams | GivenTypes) ‘)’
GivenTypes ::= AnnotType {‘,’ AnnotType}
```
The identifier `id` can be omitted only if either the `as` part or the template body is present.
If the `as` part is missing, the template body must define at least one extension method.
The identifier `id` can be omitted only if some types are implemented or the template body defines at least one extension method.
36 changes: 18 additions & 18 deletions docs/docs/reference/contextual/derivation.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ The `derives` clause generates the following given instances for the `Eq`, `Orde
companion object of `Tree`,

```scala
given [T: Eq] as Eq[Tree[T]] = Eq.derived
given [T: Ordering] as Ordering[Tree] = Ordering.derived
given [T: Show] as Show[Tree] = Show.derived
given [T: Eq] : Eq[Tree[T]] = Eq.derived
given [T: Ordering] : Ordering[Tree] = Ordering.derived
given [T: Show] : Show[Tree] = Show.derived
```

We say that `Tree` is the _deriving type_ and that the `Eq`, `Ordering` and `Show` instances are _derived instances_.
Expand All @@ -45,7 +45,7 @@ derivation support.

/** the type being mirrored */
type MirroredType

/** the type of the elements of the mirrored type */
type MirroredElemTypes

Expand Down Expand Up @@ -130,7 +130,7 @@ Note the following properties of `Mirror` types,
possibly parameterized, tuple type. Dotty's metaprogramming facilities can be used to work with these tuple types
as-is, and higher level libraries can be built on top of them.
+ The methods `ordinal` and `fromProduct` are defined in terms of `MirroredMonoType` which is the type of kind-`*`
which is obtained from `MirroredType` by wildcarding its type parameters.
which is obtained from `MirroredType` by wildcarding its type parameters.

### Type classes supporting automatic deriving

Expand All @@ -139,7 +139,7 @@ signature and implementation of a `derived` method for a type class `TC[_]` are
following form,

```scala
def derived[T] given Mirror.Of[T]: TC[T] = ...
def derived[T](given Mirror.Of[T]): TC[T] = ...
```

That is, the `derived` method takes a given parameter of (some subtype of) type `Mirror` which defines the shape of
Expand Down Expand Up @@ -175,7 +175,7 @@ we need to implement a method `Eq.derived` on the companion object of `Eq` that
a `Mirror[T]`. Here is a possible implementation,

```scala
inline given derived[T] as Eq[T] given (m: Mirror.Of[T]) = {
inline given derived[T](given m: Mirror.Of[T]): Eq[T] = {
val elemInstances = summonAll[m.MirroredElemTypes] // (1)
inline m match { // (2)
case s: Mirror.SumOf[T] => eqSum(s, elemInstances)
Expand Down Expand Up @@ -256,7 +256,7 @@ trait Eq[T] {
}

object Eq {
given as Eq[Int] {
given Eq[Int] {
def eqv(x: Int, y: Int) = x == y
}

Expand All @@ -281,7 +281,7 @@ object Eq {
}
}

inline given derived[T] as Eq[T] given (m: Mirror.Of[T]) = {
inline given derived[T](given m: Mirror.Of[T]): Eq[T] = {
val elemInstances = summonAll[m.MirroredElemTypes]
inline m match {
case s: Mirror.SumOf[T] => eqSum(s, elemInstances)
Expand All @@ -301,7 +301,7 @@ enum Opt[+T] derives Eq {

object Test extends App {
import Opt._
val eqoi = the[Eq[Opt[Int]]]
val eqoi = summon[Eq[Opt[Int]]]
assert(eqoi.eqv(Sm(23), Sm(23)))
assert(!eqoi.eqv(Sm(23), Sm(13)))
assert(!eqoi.eqv(Sm(23), Nn))
Expand All @@ -312,11 +312,11 @@ In this case the code that is generated by the inline expansion for the derived
following, after a little polishing,

```scala
given derived$Eq[T] as Eq[Opt[T]] given (eqT: Eq[T]) =
eqSum(the[Mirror[Opt[T]]],
given derived$Eq[T](given eqT: Eq[T]): Eq[Opt[T]] =
eqSum(summon[Mirror[Opt[T]]],
List(
eqProduct(the[Mirror[Sm[T]]], List(the[Eq[T]]))
eqProduct(the[Mirror[Nn.type]], Nil)
eqProduct(summon[Mirror[Sm[T]]], List(summon[Eq[T]]))
eqProduct(summon[Mirror[Nn.type]], Nil)
)
)
```
Expand All @@ -329,20 +329,20 @@ As a third example, using a higher level library such as shapeless the type clas
`derived` method as,

```scala
given eqSum[A] as Eq[A] given (inst: => K0.CoproductInstances[Eq, A]) {
given eqSum[A](given inst: => K0.CoproductInstances[Eq, A]): Eq[A] {
def eqv(x: A, y: A): Boolean = inst.fold2(x, y)(false)(
[t] => (eqt: Eq[t], t0: t, t1: t) => eqt.eqv(t0, t1)
)
}

given eqProduct[A] as Eq[A] given (inst: K0.ProductInstances[Eq, A]) {
given eqProduct[A](given inst: K0.ProductInstances[Eq, A]): Eq[A] {
def eqv(x: A, y: A): Boolean = inst.foldLeft2(x, y)(true: Boolean)(
[t] => (acc: Boolean, eqt: Eq[t], t0: t, t1: t) => Complete(!eqt.eqv(t0, t1))(false)(true)
)
}


inline def derived[A] given (gen: K0.Generic[A]): Eq[A] = gen.derive(eqSum, eqProduct)
inline def derived[A](given gen: K0.Generic[A]): Eq[A] = gen.derive(eqSum, eqProduct)
```

The framework described here enables all three of these approaches without mandating any of them.
Expand All @@ -354,7 +354,7 @@ change the code of the ADT itself. To do this, simply define an instance using
as right-hand side. E.g, to implement `Ordering` for `Option` define,

```scala
given [T: Ordering] as Ordering[Option[T]] = Ordering.derived
given [T: Ordering] : Ordering[Option[T]] = Ordering.derived
```

Assuming the `Ordering.derived` method has a given parameter of type `Mirror[T]` it will be satisfied by the
Expand Down
4 changes: 2 additions & 2 deletions docs/docs/reference/contextual/extension-methods.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ trait StringSeqOps {
```
We can make the extension method available by defining a given `StringSeqOps` instance, like this:
```scala
given ops1 as StringSeqOps
given ops1: StringSeqOps
```
Then
```scala
Expand Down Expand Up @@ -155,7 +155,7 @@ def (xs: List[List[T]]) flattened [T] =
xs.foldLeft[List[T]](Nil)(_ ++ _)

def (x: T) + [T : Numeric](y: T): T =
the[Numeric[T]].plus(x, y)
summon[Numeric[T]].plus(x, y)
```

As usual, type parameters of the extension method follow the defined method name. Nevertheless, such type parameters can already be used in the preceding parameter clause.
Expand Down
Loading