From e9e926c90b2a1ef89709f88117a4a5b3c03bf656 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 4 Jul 2019 17:35:27 +0200 Subject: [PATCH] syntax change: delegate for -> given as --- .../src/dotty/tools/dotc/ast/Desugar.scala | 2 +- .../src/dotty/tools/dotc/core/StdNames.scala | 1 + .../dotty/tools/dotc/parsing/Parsers.scala | 145 ++++++++++++------ .../src/dotty/tools/dotc/parsing/Tokens.scala | 6 +- .../tools/dotc/printing/RefinedPrinter.scala | 4 +- .../dotty/tools/dotc/typer/ImportInfo.scala | 18 +-- .../src/dotty/tools/dotc/typer/Typer.scala | 6 +- .../dotty/tools/repl/ReplCompilerTests.scala | 2 +- docs/docs/internals/syntax.md | 31 ++-- docs/docs/reference/contextual/conversions.md | 24 +-- docs/docs/reference/contextual/delegates.md | 70 ++++----- docs/docs/reference/contextual/derivation.md | 22 +-- .../reference/contextual/extension-methods.md | 26 ++-- .../reference/contextual/given-clauses.md | 14 +- .../contextual/implicit-by-name-parameters.md | 19 +-- .../implicit-function-types-spec.md | 4 +- .../contextual/implicit-function-types.md | 20 +-- .../reference/contextual/import-delegate.md | 73 +++++---- docs/docs/reference/contextual/motivation.md | 12 +- .../contextual/multiversal-equality.md | 50 +++--- .../contextual/relationship-implicits.md | 59 ++++--- docs/docs/reference/contextual/typeclasses.md | 12 +- tests/neg/i5978.scala | 8 +- tests/pos/i5978.scala | 31 ++-- tests/pos/reference/delegates.scala | 54 +++---- tests/run/boundedImports.scala | 27 ++++ 26 files changed, 422 insertions(+), 318 deletions(-) create mode 100644 tests/run/boundedImports.scala diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index fd09af8a9ee5..1f459740ebd8 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -844,7 +844,7 @@ object desugar { */ def normalizeName(mdef: MemberDef, impl: Tree)(implicit ctx: Context): Name = { var name = mdef.name - if (name.isEmpty) name = name.likeSpaced(s"${inventName(impl)}_instance".toTermName) + if (name.isEmpty) name = name.likeSpaced(s"${inventName(impl)}_given".toTermName) if (ctx.owner == defn.ScalaPackageClass && defn.reservedScalaClassNames.contains(name.toTypeName)) { def kind = if (name.isTypeName) "class" else "object" ctx.error(em"illegal redefinition of standard $kind $name", mdef.sourcePos) diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index 74c2136b0640..e331c3a84dc1 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -386,6 +386,7 @@ object StdNames { val array_length : N = "array_length" val array_update : N = "array_update" val arraycopy: N = "arraycopy" + val as: N = "as" val asTerm: N = "asTerm" val asModule: N = "asModule" val asMethod: N = "asMethod" diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 56f492584509..f3a067ec13f1 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -194,12 +194,16 @@ object Parsers { /** Is current token a hard or soft modifier (in modifier position or not)? */ def isModifier: Boolean = modifierTokens.contains(in.token) || in.isSoftModifier - def isBindingIntro: Boolean = - canStartBindingTokens.contains(in.token) && - !in.isSoftModifierInModifierPosition + def isBindingIntro: Boolean = { + in.token match { + case USCORE | LPAREN => true + case IDENTIFIER | BACKQUOTED_IDENT => in.lookaheadIn(BitSet(COLON, ARROW)) + case _ => false + } + } && !in.isSoftModifierInModifierPosition def isExprIntro: Boolean = - if (in.token == IMPLIED) in.lookaheadIn(BitSet(MATCH)) + if (in.token == IMPLIED || in.token == GIVEN) in.lookaheadIn(BitSet(MATCH)) else (canStartExpressionTokens.contains(in.token) && !in.isSoftModifierInModifierPosition) def isDefIntro(allowedMods: BitSet, excludedSoftModifiers: Set[TermName] = Set.empty): Boolean = @@ -1274,7 +1278,7 @@ object Parsers { * | SimpleExpr1 ArgumentExprs `=' Expr * | Expr2 * | [‘inline’] Expr2 `match' `{' CaseClauses `}' - * | `delegate' `match' `{' ImplicitCaseClauses `}' + * | `given' `match' `{' ImplicitCaseClauses `}' * Bindings ::= `(' [Binding {`,' Binding}] `)' * Binding ::= (id | `_') [`:' Type] * Expr2 ::= PostfixExpr [Ascription] @@ -1293,7 +1297,7 @@ object Parsers { if (in.token == MATCH) impliedMatch(start, imods) else implicitClosure(start, location, imods) } - else if(in.token == IMPLIED) { + else if (in.token == IMPLIED || in.token == GIVEN) { in.nextToken() if (in.token == MATCH) impliedMatch(start, EmptyModifiers) @@ -1478,7 +1482,7 @@ object Parsers { */ def impliedMatch(start: Int, imods: Modifiers) = { def markFirstIllegal(mods: List[Mod]) = mods match { - case mod :: _ => syntaxError(em"illegal modifier for delegate match", mod.span) + case mod :: _ => syntaxError(em"illegal modifier for given match", mod.span) case _ => } imods.mods match { @@ -1493,7 +1497,7 @@ object Parsers { case pat => isVarPattern(pat) } if (!isImplicitPattern(pat)) - syntaxError(em"not a legal pattern for a delegate match", pat.span) + syntaxError(em"not a legal pattern for a given match", pat.span) } result } @@ -2303,9 +2307,59 @@ object Parsers { def paramClauses(ofClass: Boolean = false, ofCaseClass: Boolean = false, ofInstance: Boolean = false): List[List[ValDef]] = { + + def followingIsParamClause: Boolean = { + val lookahead = in.lookaheadScanner + lookahead.nextToken() + paramIntroTokens.contains(lookahead.token) && { + lookahead.token != IDENTIFIER || + lookahead.name == nme.inline || { + lookahead.nextToken() + lookahead.token == COLON + } + } + } + + /** For given instance definitions we have a disambiguation problem: + * given A as B + * given C ... + * Is the second line a parameter `given C` for the first `given` definition, or is it + * a second `given` definition? We only know if we find a `for` or `as` in `...` + * The same problem arises for + * class A + * given C ... + * For method definitions we do not have this problem since a parameter clause + * in a method definition is always followed by something else. So in + * def m(...) + * given C ... + * we know that `given` must start a parameter list. It cannot be a new given` definition. + */ + def followingIsInstanceDef = + (ofClass || ofInstance) && { + val lookahead = in.lookaheadScanner // skips newline on startup + lookahead.nextToken() // skip the `given` + if (lookahead.token == IDENTIFIER || lookahead.token == BACKQUOTED_IDENT) { + lookahead.nextToken() + if (lookahead.token == LBRACKET) { + lookahead.nextToken() + var openBrackets = 1 + while (openBrackets > 0 && lookahead.token != EOF) { + if (lookahead.token == LBRACKET) openBrackets += 1 + else if (lookahead.token == RBRACKET) openBrackets -= 1 + lookahead.nextToken() + } + } + } + lookahead.token == FOR || + lookahead.token == IDENTIFIER && lookahead.name == nme.as + } + def recur(firstClause: Boolean, nparams: Int, contextualOnly: Boolean): List[List[ValDef]] = { var initialMods = EmptyModifiers + val isNewLine = in.token == NEWLINE newLineOptWhenFollowedBy(LPAREN) + if (in.token == NEWLINE && in.next.token == GIVEN && !followingIsInstanceDef) + in.nextToken() if (in.token == GIVEN) { in.nextToken() initialMods |= Given @@ -2314,22 +2368,10 @@ object Parsers { in.nextToken() initialMods |= Erased } - val isContextual = initialMods.is(Given) + val isGiven = initialMods.is(Given) newLineOptWhenFollowedBy(LPAREN) - def isParamClause: Boolean = - !isContextual || { - val lookahead = in.lookaheadScanner - lookahead.nextToken() - paramIntroTokens.contains(lookahead.token) && { - lookahead.token != IDENTIFIER || - lookahead.name == nme.inline || { - lookahead.nextToken() - lookahead.token == COLON - } - } - } - if (in.token == LPAREN && isParamClause) { - if (contextualOnly && !isContextual) + if (in.token == LPAREN && (!isGiven || followingIsParamClause)) { + if (contextualOnly && !isGiven) if (ofInstance) syntaxError(em"parameters of instance definitions must come after `given'") else syntaxError(em"normal parameters cannot come after `given' clauses") val params = paramClause( @@ -2338,15 +2380,15 @@ object Parsers { firstClause = firstClause, initialMods = initialMods) val lastClause = params.nonEmpty && params.head.mods.flags.is(Implicit) - params :: (if (lastClause) Nil else recur(firstClause = false, nparams + params.length, isContextual)) + params :: (if (lastClause) Nil else recur(firstClause = false, nparams + params.length, isGiven)) } - else if (isContextual) { + else if (isGiven) { val tps = commaSeparated(() => annotType()) var counter = nparams def nextIdx = { counter += 1; counter } val paramFlags = if (ofClass) Private | Local | ParamAccessor else Param val params = tps.map(makeSyntheticParameter(nextIdx, _, paramFlags | Synthetic | Given)) - params :: recur(firstClause = false, nparams + params.length, isContextual) + params :: recur(firstClause = false, nparams + params.length, isGiven) } else Nil } @@ -2360,12 +2402,12 @@ object Parsers { type ImportConstr = (Boolean, Tree, List[Tree]) => Tree - /** Import ::= import [delegate] [ImportExpr {`,' ImportExpr} - * Export ::= export [delegate] [ImportExpr {`,' ImportExpr} + /** Import ::= `import' [`given'] [ImportExpr {`,' ImportExpr} + * Export ::= `export' [`given'] [ImportExpr {`,' ImportExpr} */ def importClause(leading: Token, mkTree: ImportConstr): List[Tree] = { val offset = accept(leading) - val importDelegate = in.token == IMPLIED + val importDelegate = in.token == IMPLIED || in.token == GIVEN if (importDelegate) in.nextToken() commaSeparated(importExpr(importDelegate, mkTree)) match { case t :: rest => @@ -2384,15 +2426,21 @@ object Parsers { /** ImportSelectors ::= `{' {ImportSelector `,'} FinalSelector ‘}’ * FinalSelector ::= ImportSelector - * | ‘_’ - * | ‘for’ InfixType {‘,’ InfixType} + * | ‘_’ [‘:’ Type] */ def importSelectors(): List[Tree] = in.token match { case USCORE => - wildcardIdent() :: Nil + atSpan(in.skipToken()) { + val id = Ident(nme.WILDCARD) + if (in.token == COLON) { + in.nextToken() + TypeBoundsTree(EmptyTree, typ()) + } + else id + } :: Nil case FOR => if (!importDelegate) - syntaxError(em"`for` qualifier only allowed in `import delegate`") + syntaxError(em"`for` qualifier only allowed in `import given`") atSpan(in.skipToken()) { var t = infixType() while (in.token == COMMA) { @@ -2696,7 +2744,7 @@ object Parsers { /** TmplDef ::= ([‘case’] ‘class’ | ‘trait’) ClassDef * | [‘case’] ‘object’ ObjectDef * | ‘enum’ EnumDef - * | ‘instance’ InstanceDef + * | ‘given’ GivenDef */ def tmplDef(start: Int, mods: Modifiers): Tree = { in.token match { @@ -2712,8 +2760,8 @@ object Parsers { objectDef(start, posMods(start, mods | Case | Module)) case ENUM => enumDef(start, posMods(start, mods | Enum)) - case IMPLIED => - instanceDef(start, mods, atSpan(in.skipToken()) { Mod.Delegate() }) + case IMPLIED | GIVEN => + instanceDef(in.token == GIVEN, start, mods, atSpan(in.skipToken()) { Mod.Delegate() }) case _ => syntaxErrorOrIncomplete(ExpectedStartOfTopLevelDefinition()) EmptyTree @@ -2805,17 +2853,16 @@ object Parsers { Template(constr, parents, Nil, EmptyValDef, Nil) } - /** InstanceDef ::= [id] [DefTypeParamClause] InstanceBody - * InstanceParams ::= [DefTypeParamClause] {GivenParamClause} - * InstanceBody ::= [‘for’ ConstrApp {‘,’ ConstrApp }] {GivenParamClause} [TemplateBody] - * | ‘for’ Type {GivenParamClause} ‘=’ Expr + /** GivenDef ::= [id] [DefTypeParamClause] GivenBody + * GivenBody ::= [‘as ConstrApp {‘,’ ConstrApp }] {GivenParamClause} [TemplateBody] + * | ‘as’ Type {GivenParamClause} ‘=’ Expr */ - def instanceDef(start: Offset, mods: Modifiers, instanceMod: Mod) = atSpan(start, nameStart) { + def instanceDef(newStyle: Boolean, start: Offset, mods: Modifiers, instanceMod: Mod) = atSpan(start, nameStart) { var mods1 = addMod(mods, instanceMod) - val name = if (isIdent) ident() else EmptyTermName + val name = if (isIdent && (!newStyle || in.name != nme.as)) ident() else EmptyTermName val tparams = typeParamClauseOpt(ParamOwner.Def) val parents = - if (in.token == FOR) { + if (!newStyle && in.token == FOR || isIdent(nme.as)) { // for the moment, accept both `given for` and `given as` in.nextToken() tokenSeparated(COMMA, constrApp) } @@ -3122,8 +3169,16 @@ object Parsers { setLastStatOffset() if (in.token == IMPORT) stats ++= importClause(IMPORT, Import) - else if (in.token == GIVEN) - stats += implicitClosure(in.offset, Location.InBlock, modifiers(closureMods)) + else if (in.token == GIVEN) { + val start = in.offset + val mods = modifiers(closureMods) + mods.mods match { + case givenMod :: Nil if !isBindingIntro => + stats += instanceDef(true, start, EmptyModifiers, Mod.Delegate().withSpan(givenMod.span)) + case _ => + stats += implicitClosure(in.offset, Location.InBlock, mods) + } + } else if (isExprIntro) stats += expr(Location.InBlock) else if (isDefIntro(localModifierTokens, excludedSoftModifiers = Set(nme.`opaque`))) diff --git a/compiler/src/dotty/tools/dotc/parsing/Tokens.scala b/compiler/src/dotty/tools/dotc/parsing/Tokens.scala index 6f20c80793ee..0e7d9754fcde 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Tokens.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Tokens.scala @@ -216,16 +216,14 @@ object Tokens extends TokensCommon { USCORE, NULL, THIS, SUPER, TRUE, FALSE, RETURN, QUOTEID, XMLSTART) final val canStartExpressionTokens: TokenSet = atomicExprTokens | BitSet( - LBRACE, LPAREN, QUOTE, IF, DO, WHILE, FOR, NEW, TRY, THROW, IMPLIED) + LBRACE, LPAREN, QUOTE, IF, DO, WHILE, FOR, NEW, TRY, THROW, IMPLIED, GIVEN) final val canStartTypeTokens: TokenSet = literalTokens | identifierTokens | BitSet( THIS, SUPER, USCORE, LPAREN, AT) - final val canStartBindingTokens: TokenSet = identifierTokens | BitSet(USCORE, LPAREN) - final val templateIntroTokens: TokenSet = BitSet(CLASS, TRAIT, OBJECT, ENUM, CASECLASS, CASEOBJECT) - final val dclIntroTokens: TokenSet = BitSet(DEF, VAL, VAR, TYPE, IMPLIED) + final val dclIntroTokens: TokenSet = BitSet(DEF, VAL, VAR, TYPE, IMPLIED, GIVEN) final val defIntroTokens: TokenSet = templateIntroTokens | dclIntroTokens diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 1d02dd716b11..4b842119e6a9 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -319,7 +319,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case _ => tree } - def importText(deleg: Boolean, expr: Tree, selectors: List[Tree]) = { + def importText(givenOnly: Boolean, expr: Tree, selectors: List[Tree]) = { def selectorText(sel: Tree): Text = sel match { case Thicket(l :: r :: Nil) => toTextGlobal(l) ~ " => " ~ toTextGlobal(r) case _: Ident => toTextGlobal(sel) @@ -329,7 +329,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case id :: Nil => toText(id) case _ => "{" ~ Text(selectors map selectorText, ", ") ~ "}" } - (keywordText("delegate ") provided deleg) ~ + (keywordText("given ") provided givenOnly) ~ toTextLocal(expr) ~ "." ~ selectorsText } diff --git a/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala b/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala index ba16b0a8e34d..313adfbf5ccb 100644 --- a/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala +++ b/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala @@ -91,7 +91,7 @@ class ImportInfo(symf: Context => Symbol, val selectors: List[untpd.Tree], myForwardMapping = myForwardMapping.updated(name, name) myReverseMapping = myReverseMapping.updated(name, name) case TypeBoundsTree(_, tpt) => - myWildcardImport = true // details are handled separately in impliedBounds + myWildcardImport = true // details are handled separately in wildcardBounds } recur(sels1) case nil => @@ -99,18 +99,18 @@ class ImportInfo(symf: Context => Symbol, val selectors: List[untpd.Tree], recur(selectors) } - private[this] var myDelegateBound: Type = null + private[this] var myWildcardBound: Type = null - def impliedBound(implicit ctx: Context): Type = { - if (myDelegateBound == null) - myDelegateBound = selectors.lastOption match { + def wildcardBound(implicit ctx: Context): Type = { + if (myWildcardBound == null) + myWildcardBound = selectors.lastOption match { case Some(TypeBoundsTree(_, untpd.TypedSplice(tpt))) => tpt.tpe case Some(TypeBoundsTree(_, tpt)) => - myDelegateBound = NoType + myWildcardBound = NoType ctx.typer.typedAheadType(tpt).tpe case _ => NoType } - myDelegateBound + myWildcardBound } private def implicitFlags(implicit ctx: Context) = @@ -128,8 +128,8 @@ class ImportInfo(symf: Context => Symbol, val selectors: List[untpd.Tree], val renamed = forwardMapping(ref.name) if (renamed == ref.name) ref :: Nil else if (renamed != null) new RenamedImplicitRef(ref, renamed) :: Nil - else if (!impliedBound.exists || - normalizedCompatible(ref, impliedBound, keepConstraint = false)) ref :: Nil + else if (!wildcardBound.exists || + normalizedCompatible(ref, wildcardBound, keepConstraint = false)) ref :: Nil else Nil } } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 4cdd973788ae..80ee15c41599 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -189,9 +189,9 @@ class Typer extends Namer var excl = EmptyFlags if (imp.importDelegate) reqd |= Delegate else excl |= Delegate var denot = pre.memberBasedOnFlags(name, reqd, excl).accessibleFrom(pre)(refctx) - if (checkBounds && imp.impliedBound.exists) + if (checkBounds && imp.wildcardBound.exists) denot = denot.filterWithPredicate(mbr => - NoViewsAllowed.normalizedCompatible(mbr.info, imp.impliedBound, keepConstraint = false)) + NoViewsAllowed.normalizedCompatible(mbr.info, imp.wildcardBound, keepConstraint = false)) // Pass refctx so that any errors are reported in the context of the // reference instead of the @@ -234,7 +234,7 @@ class Typer extends Namer */ def wildImportRef(imp: ImportInfo)(implicit ctx: Context): Type = if (imp.isWildcardImport && !imp.excluded.contains(name.toTermName) && name != nme.CONSTRUCTOR) - selection(imp, name, checkBounds = imp.importDelegate) + selection(imp, name, checkBounds = true) else NoType /** Is (some alternative of) the given predenotation `denot` diff --git a/compiler/test/dotty/tools/repl/ReplCompilerTests.scala b/compiler/test/dotty/tools/repl/ReplCompilerTests.scala index cc28d2deff2f..65d65629d8e8 100644 --- a/compiler/test/dotty/tools/repl/ReplCompilerTests.scala +++ b/compiler/test/dotty/tools/repl/ReplCompilerTests.scala @@ -132,7 +132,7 @@ class ReplCompilerTests extends ReplTest { fromInitialState { implicit state => run("delegate for Int = 10") } .andThen { implicit state => assertEquals( - "def Int_instance: Int", + "def Int_given: Int", storedOutput().trim ) run("implicitly[Int]") diff --git a/docs/docs/internals/syntax.md b/docs/docs/internals/syntax.md index 887bd2256414..9844bd296abb 100644 --- a/docs/docs/internals/syntax.md +++ b/docs/docs/internals/syntax.md @@ -92,12 +92,12 @@ semi ::= ‘;’ | nl {nl} ### Regular keywords ``` -abstract case catch class def delegate do else -enum export extends false final finally for given -if implicit import lazy match new null object -package private protected override return super sealed then -throw trait true try type val var while -with yield +abstract case catch class def do else enum +export extends false final finally for given if +implicit import lazy match new null object package +private protected override return super sealed then throw +trait true try type val var while with +yield : = <- => <: :> # @ =>> ``` @@ -105,7 +105,7 @@ with yield ### Soft keywords ``` -derives inline opaque +as derives inline opaque ~ * | & + - ``` @@ -203,7 +203,7 @@ Expr1 ::= ‘if’ ‘(’ Expr ‘)’ {nl} | SimpleExpr1 ArgumentExprs ‘=’ Expr Assign(expr, expr) | Expr2 | [‘inline’] Expr2 ‘match’ ‘{’ CaseClauses ‘}’ Match(expr, cases) -- point on match - | ‘implicit’ ‘match’ ‘{’ ImplicitCaseClauses ‘}’ + | ‘given’ ‘match’ ‘{’ ImplicitCaseClauses ‘}’ Expr2 ::= PostfixExpr [Ascription] Ascription ::= ‘:’ InfixType Typed(expr, tp) | ‘:’ Annotation {Annotation} Typed(expr, Annotated(EmptyTree, annot)*) @@ -336,14 +336,13 @@ AccessQualifier ::= ‘[’ (id | ‘this’) ‘]’ Annotation ::= ‘@’ SimpleType {ParArgumentExprs} Apply(tpe, args) -Import ::= ‘import’ [‘delegate’] ImportExpr {‘,’ ImportExpr} +Import ::= ‘import’ [‘given’] ImportExpr {‘,’ ImportExpr} ImportExpr ::= StableId ‘.’ (id | ‘_’ | ImportSelectors) Import(expr, sels) ImportSelectors ::= ‘{’ {ImportSelector ‘,’} FinalSelector ‘}’ FinalSelector ::= ImportSelector Ident(name) - | ‘_’ Pair(id, id) - | ‘for’ InfixType {‘,’ InfixType} TypeBoundsTree(EmptyTree, tpt) + | ‘_’ [‘:’ Type] TypeBoundsTree(EmptyTree, tpt) ImportSelector ::= id [‘=>’ id | ‘=>’ ‘_’] -Export ::= ‘export’ [‘delegate’] ImportExpr {‘,’ ImportExpr} +Export ::= ‘export’ [‘given’] ImportExpr {‘,’ ImportExpr} ``` ### Declarations and Definitions @@ -379,16 +378,16 @@ DefDef ::= DefSig [(‘:’ | ‘<:’) Type] ‘=’ Expr TmplDef ::= ([‘case’] ‘class’ | ‘trait’) ClassDef | [‘case’] ‘object’ ObjectDef | ‘enum’ EnumDef - | ‘delegate’ DelegateDef + | ‘given’ GivenDef | Export ClassDef ::= id ClassConstr [Template] ClassDef(mods, name, tparams, templ) ClassConstr ::= [ClsTypeParamClause] [ConstrMods] ClsParamClauses with DefDef(_, , Nil, vparamss, EmptyTree, EmptyTree) as first stat ConstrMods ::= {Annotation} [AccessModifier] ObjectDef ::= id [Template] ModuleDef(mods, name, template) // no constructor EnumDef ::= id ClassConstr InheritClauses EnumBody EnumDef(mods, name, tparams, template) -DelegateDef ::= [id] [DefTypeParamClause] DelegateBody -DelegateBody ::= [‘for’ ConstrApp {‘,’ ConstrApp }] {GivenParamClause} [TemplateBody] - | ‘for’ Type {GivenParamClause} ‘=’ Expr +GivenDef ::= [id] [DefTypeParamClause] GivenBody +GivenBody ::= [‘as ConstrApp {‘,’ ConstrApp }] {GivenParamClause} [TemplateBody] + | ‘as’ Type {GivenParamClause} ‘=’ Expr Template ::= InheritClauses [TemplateBody] Template(constr, parents, self, stats) InheritClauses ::= [‘extends’ ConstrApps] [‘derives’ QualId {‘,’ QualId}] ConstrApps ::= ConstrApp {‘with’ ConstrApp} diff --git a/docs/docs/reference/contextual/conversions.md b/docs/docs/reference/contextual/conversions.md index 802c27569a61..255bde2fd665 100644 --- a/docs/docs/reference/contextual/conversions.md +++ b/docs/docs/reference/contextual/conversions.md @@ -3,20 +3,20 @@ layout: doc-page title: "Implicit Conversions" --- -Implicit conversions are defined by delegates for the `scala.Conversion` class. +Implicit conversions are defined by given instances of the `scala.Conversion` class. This class is defined in package `scala` as follows: ```scala abstract class Conversion[-T, +U] extends (T => U) ``` For example, here is an implicit conversion from `String` to `Token`: ```scala -delegate for Conversion[String, Token] { +given as Conversion[String, Token] { def apply(str: String): Token = new KeyWord(str) } ``` -Using an alias delegate this can be expressed more concisely as: +Using an alias this can be expressed more concisely as: ```scala -delegate for Conversion[String, Token] = new KeyWord(_) +given as Conversion[String, Token] = new KeyWord(_) ``` An implicit conversion is applied automatically by the compiler in three situations: @@ -25,11 +25,11 @@ An implicit conversion is applied automatically by the compiler in three situati 3. In an application `e.m(args)` with `e` of type `T`, if `T` does define some member(s) named `m`, but none of these members can be applied to the arguments `args`. -In the first case, the compiler looks for a delegate for -`scala.Conversion` that maps an argument of type `T` to type `S`. In the second and third -case, it looks for a delegate for `scala.Conversion` that maps an argument of type `T` +In the first case, the compiler looks for a given `scala.Conversion` that maps +an argument of type `T` to type `S`. In the second and third +case, it looks for a given `scala.Conversion` that maps an argument of type `T` to a type that defines a member `m` which can be applied to `args` if present. -If such a delegate `C` is found, the expression `e` is replaced by `C.apply(e)`. +If such an instance `C` is given, the expression `e` is replaced by `C.apply(e)`. ## Examples @@ -37,7 +37,7 @@ If such a delegate `C` is found, 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 -delegate int2Integer for Conversion[Int, java.lang.Integer] = +given int2Integer as Conversion[Int, java.lang.Integer] = java.lang.Integer.valueOf(_) ``` @@ -59,9 +59,9 @@ object Completions { // // CompletionArg.fromStatusCode(statusCode) - delegate fromString for Conversion[String, CompletionArg] = Error(_) - delegate fromFuture for Conversion[Future[HttpResponse], CompletionArg] = Response(_) - delegate fromStatusCode for Conversion[Future[StatusCode], CompletionArg] = Status(_) + given fromString as Conversion[String, CompletionArg] = Error(_) + given fromFuture as Conversion[Future[HttpResponse], CompletionArg] = Response(_) + given fromStatusCode as Conversion[Future[StatusCode], CompletionArg] = Status(_) } import CompletionArg._ diff --git a/docs/docs/reference/contextual/delegates.md b/docs/docs/reference/contextual/delegates.md index 60562b5be8de..3bec0f71a652 100644 --- a/docs/docs/reference/contextual/delegates.md +++ b/docs/docs/reference/contextual/delegates.md @@ -1,9 +1,9 @@ --- layout: doc-page -title: "Delegates" +title: "Given Instances" --- -Delegates define "canonical" values of certain types +Given instances (or, simply, "givens") define "canonical" values of certain types that serve for synthesizing arguments to [given clauses](./given-clauses.html). Example: ```scala @@ -13,12 +13,13 @@ trait Ord[T] { def (x: T) > (y: T) = compare(x, y) > 0 } -delegate IntOrd for Ord[Int] { +given IntOrd as Ord[Int] { def compare(x: Int, y: Int) = if (x < y) -1 else if (x > y) +1 else 0 } -delegate ListOrd[T] for Ord[List[T]] given (ord: Ord[T]) { +given ListOrd[T] as Ord[List[T]] given (ord: Ord[T]) { + def compare(xs: List[T], ys: List[T]): Int = (xs, ys) match { case (Nil, Nil) => 0 case (Nil, _) => -1 @@ -29,63 +30,62 @@ delegate ListOrd[T] for Ord[List[T]] given (ord: Ord[T]) { } } ``` -This code defines a trait `Ord` with two delegate definitions. `IntOrd` defines -a delegate for the type `Ord[Int]` whereas `ListOrd[T]` defines delegates -for `Ord[List[T]]` for all types `T` that come with a delegate for `Ord[T]` themselves. -The `given` clause in `ListOrd` defines an implicit parameter. +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. Given clauses are further explained in the [next section](./given-clauses.html). -## Anonymous Delegates +## Anonymous Given Instances -The name of a delegate can be left out. So the delegate definitions +The name of a given instance can be left out. So the definitions of the last section can also be expressed like this: ```scala -delegate for Ord[Int] { ... } -delegate [T] for Ord[List[T]] given (ord: Ord[T]) { ... } +given as Ord[Int] { ... } +given [T] as Ord[List[T]] given Ord[T] { ... } ``` -If the name of a delegate is missing, the compiler will synthesize a name from -the type(s) in the `for` clause. +If the name of a given is missing, the compiler will synthesize a name from +the type(s) in the `as` clause. -## Alias Delegates +## Alias Givens -An alias can be used to define a delegate that is equal to some expression. E.g.: +An alias can be used to define a given instance that is equal to some expression. E.g.: ```scala -delegate global for ExecutionContext = new ForkJoinPool() +given global as ExecutionContext = new ForkJoinPool() ``` -This creates a delegate `global` of type `ExecutionContext` that resolves to the right hand side `new ForkJoinPool()`. +This creates a given `global` of type `ExecutionContext` that resolves to the right +hand side `new ForkJoinPool()`. The first time `global` is accessed, a new `ForkJoinPool` is created, which is then returned for this and all subsequent accesses to `global`. -Alias delegates can be anonymous, e.g. +Alias givens can be anonymous, e.g. ```scala -delegate for Position = enclosingTree.position -delegate for Context given (outer: Context) = - outer.withOwner(currentOwner) +given as Position = enclosingTree.position +given as Context given (outer: Context) = outer.withOwner(currentOwner) ``` -An alias delegate can have type parameters and given clauses just like any other delegate, but it can only implement a single type. +An alias given can have type parameters and given clauses just like any other given instance, but it can only implement a single type. -## Delegate Instantiation +## Given Instance Initialization -A delegate without type parameters or given clause is instantiated on-demand, the first +A given instance without type parameters or given clause is initialized on-demand, the first time it is accessed. It is not required to ensure safe publication, which means that -different threads might create different delegates for the same `delegate` clause. -If a `delegate` clause has type parameters or a given clause, a fresh delegate is -created for each reference. +different threads might create different instances for the same `given` definition. +If a `given` definition has type parameters or a given clause, a fresh instance is created for each reference. ## Syntax -Here is the new syntax of delegate clauses, seen as a delta from the [standard context free syntax of Scala 3](http://dotty.epfl.ch/docs/internals/syntax.html). +Here is the new syntax of given instances, seen as a delta from the [standard context free syntax of Scala 3](http://dotty.epfl.ch/docs/internals/syntax.html). ``` TmplDef ::= ... - | ‘delegate’ DelegateDef -DelegateDef ::= [id] [DefTypeParamClause] DelegateBody -DelegateBody ::= [‘for’ ConstrApp {‘,’ ConstrApp }] {GivenParamClause} [TemplateBody] - | ‘for’ Type {GivenParamClause} ‘=’ Expr + | ‘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) GivenTypes ::= AnnotType {‘,’ AnnotType} ``` -The identifier `id` can be omitted only if either the `for` part or the template body is present. -If the `for` part is missing, the template body must define at least one extension method. +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. diff --git a/docs/docs/reference/contextual/derivation.md b/docs/docs/reference/contextual/derivation.md index 7aeb214f6cba..779ef568ab1e 100644 --- a/docs/docs/reference/contextual/derivation.md +++ b/docs/docs/reference/contextual/derivation.md @@ -3,18 +3,18 @@ layout: doc-page title: Typeclass Derivation --- -Typeclass derivation is a way to generate delegates for certain type classes automatically or with minimal code hints. A type class in this sense is any trait or class with a type parameter that describes the type being operated on. Commonly used examples are `Eql`, `Ordering`, `Show`, or `Pickling`. Example: +Typeclass derivation is a way to generate given instances for certain type classes automatically or with minimal code hints. A type class in this sense is any trait or class with a type parameter that describes the type being operated on. Commonly used examples are `Eql`, `Ordering`, `Show`, or `Pickling`. Example: ```scala enum Tree[T] derives Eql, Ordering, Pickling { case Branch(left: Tree[T], right: Tree[T]) case Leaf(elem: T) } ``` -The `derives` clause generates delegates for the `Eql`, `Ordering`, and `Pickling` traits in the companion object `Tree`: +The `derives` clause generates given instances for the `Eql`, `Ordering`, and `Pickling` traits in the companion object `Tree`: ```scala -delegate [T: Eql] for Eql[Tree[T]] = Eql.derived -delegate [T: Ordering] for Ordering[Tree[T]] = Ordering.derived -delegate [T: Pickling] for Pickling[Tree[T]] = Pickling.derived +given [T: Eql] as Eql[Tree[T]] = Eql.derived +given [T: Ordering] as Ordering[Tree[T]] = Ordering.derived +given [T: Pickling] as Pickling[Tree[T]] = Pickling.derived ``` ### Deriving Types @@ -34,7 +34,7 @@ case class Some[T] extends Option[T] case object None extends Option[Nothing] ``` -The generated typeclass delegates are placed in the companion objects `Labelled` and `Option`, respectively. +The generated typeclass instances are placed in the companion objects `Labelled` and `Option`, respectively. ### Derivable Types @@ -43,7 +43,7 @@ A trait or class can appear in a `derives` clause if its companion object define def derived[T] given Mirror.Of[T] = ... ``` That is, the `derived` method takes an implicit parameter of (some subtype of) type `Mirror` that defines the shape of the deriving type `T` and it computes the typeclass implementation according -to that shape. A `Mirror` delegate is generated automatically for +to that shape. A given `Mirror` instance is generated automatically for - case classes and objects, - enums and enum cases, @@ -101,10 +101,10 @@ is represented as `T *: Unit` since there is no direct syntax for such tuples: ` ### The Generic Typeclass -For every class `C[T_1,...,T_n]` with a `derives` clause, the compiler generates in the companion object of `C` a delegate for `Generic[C[T_1,...,T_n]]` that follows +For every class `C[T_1,...,T_n]` with a `derives` clause, the compiler generates in the companion object of `C` a given instance for `Generic[C[T_1,...,T_n]]` that follows the outline below: ```scala -delegate [T_1, ..., T_n] for Generic[C[T_1,...,T_n]] { +given [T_1, ..., T_n] as Generic[C[T_1,...,T_n]] { type Shape = ... ... } @@ -122,7 +122,7 @@ would produce: object Result { import scala.compiletime.Shape._ - delegate [T, E] for Generic[Result[T, E]] { + given [T, E] as Generic[Result[T, E]] { type Shape = Cases[( Case[Ok[T], T *: Unit], Case[Err[E], E *: Unit] @@ -322,7 +322,7 @@ calling the `error` method defined in `scala.compiletime`. **Example:** Here is a slightly polished and compacted version of the code that's generated by inline expansion for the derived `Eql` delegate for class `Tree`. ```scala -delegate [T] for Eql[Tree[T]] given (elemEq: Eql[T]) { +given [T] as Eql[Tree[T]] where (elemEq: Eql[T]) { def eql(x: Tree[T], y: Tree[T]): Boolean = { val ev = the[Generic[Tree[T]]] val mx = ev.reflect(x) diff --git a/docs/docs/reference/contextual/extension-methods.md b/docs/docs/reference/contextual/extension-methods.md index ee8ac4d91356..68ffd95e7d86 100644 --- a/docs/docs/reference/contextual/extension-methods.md +++ b/docs/docs/reference/contextual/extension-methods.md @@ -36,7 +36,7 @@ When is an extension method applicable? There are two possibilities. - An extension method is applicable if it is visible under a simple name, by being defined or inherited or imported in a scope enclosing the application. - - An extension method is applicable if it is a member of some delegate that's eligible at the point of the application. + - An extension method is applicable if it is a member of some given instance at the point of the application. As an example, consider an extension method `longestStrings` on `String` defined in a trait `StringSeqOps`. @@ -48,15 +48,15 @@ trait StringSeqOps { } } ``` -We can make the extension method available by defining a delegate for `StringSeqOps`, like this: +We can make the extension method available by defining a given `StringSeqOps` instance, like this: ```scala -delegate ops1 for StringSeqOps +given ops1 as StringSeqOps ``` Then ```scala List("here", "is", "a", "list").longestStrings ``` -is legal everywhere `ops1` is available as a delegate. Alternatively, we can define `longestStrings` as a member of a normal object. But then the method has to be brought into scope to be usable as an extension method. +is legal everywhere `ops1` is available. Alternatively, we can define `longestStrings` as a member of a normal object. But then the method has to be brought into scope to be usable as an extension method. ```scala object ops2 extends StringSeqOps @@ -69,32 +69,32 @@ Assume a selection `e.m[Ts]` where `m` is not a member of `e`, where the type ar and where `T` is the expected type. The following two rewritings are tried in order: 1. The selection is rewritten to `m[Ts](e)`. - 2. If the first rewriting does not typecheck with expected type `T`, and there is a delegate `d` - in either the current scope or in the implicit scope of `T`, and `d` defines an extension - method named `m`, then selection is expanded to `d.m[Ts](e)`. + 2. If the first rewriting does not typecheck with expected type `T`, and there is a given instance `i` + in either the current scope or in the implicit scope of `T`, and `i` defines an extension + method named `m`, then selection is expanded to `i.m[Ts](e)`. This second rewriting is attempted at the time where the compiler also tries an implicit conversion from `T` to a type containing `m`. If there is more than one way of rewriting, an ambiguity error results. So `circle.circumference` translates to `CircleOps.circumference(circle)`, provided -`circle` has type `Circle` and `CircleOps` is an eligible delegate (i.e. it is visible at the point of call or it is defined in the companion object of `Circle`). +`circle` has type `Circle` and `CircleOps` is given (i.e. it is visible at the point of call or it is defined in the companion object of `Circle`). -### Delegates for Extension Methods +### Given Instances for Extension Methods -Delegates that define extension methods can also be defined without a `for` clause. E.g., +Given instances that define extension methods can also be defined without a `for` clause. E.g., ```scala -delegate StringOps { +given StringOps { def (xs: Seq[String]) longestStrings: Seq[String] = { val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) } } -delegate { +given { def (xs: List[T]) second[T] = xs.tail.head } ``` -If such delegates are anonymous (as in the second clause), their name is synthesized from the name +If such given instances are anonymous (as in the second clause), their name is synthesized from the name of the first defined extension method. ### Operators diff --git a/docs/docs/reference/contextual/given-clauses.md b/docs/docs/reference/contextual/given-clauses.md index 5e618e71027d..292b843afaee 100644 --- a/docs/docs/reference/contextual/given-clauses.md +++ b/docs/docs/reference/contextual/given-clauses.md @@ -1,6 +1,6 @@ --- layout: doc-page -title: "Given Clauses" +title: "Given Parameters" --- Functional programming tends to express most dependencies as simple function parameterization. @@ -9,7 +9,7 @@ call trees where the same value is passed over and over again in long call chain functions. Given clauses can help here since they enable the compiler to synthesize repetitive arguments instead of the programmer having to write them explicitly. -For example, given the [delegates](./delegates.md) defined previously, +For example, with the [given instances](./delegates.md) defined previously, a maximum function that works for any arguments for which an ordering exists can be defined as follows: ```scala def max[T](x: T, y: T) given (ord: Ord[T]): T = @@ -73,8 +73,8 @@ def f given (u: Universe) given (x: u.Context) = ... However, all `given` clauses in a definition must come after any normal parameter clauses. Multiple given clauses are matched left-to-right in applications. Example: ```scala -delegate global for Universe { type Context = ... } -delegate ctx for global.Context { ... } +given global as Universe { type Context = ... } +given ctx as global.Context { ... } ``` Then the following calls are all valid (and normalize to the last one) ```scala @@ -84,10 +84,10 @@ f ``` But `f given ctx` would give a type error. -## Summoning Delegates +## Summoning Instances -A method `the` in `Predef` returns the delegate for a given type. For example, -the delegate for `Ord[List[Int]]` is produced by +A method `the` in `Predef` returns the given instance of a specific type. For example, +the given instance for `Ord[List[Int]]` is produced by ```scala the[Ord[List[Int]]] // reduces to ListOrd given IntOrd ``` diff --git a/docs/docs/reference/contextual/implicit-by-name-parameters.md b/docs/docs/reference/contextual/implicit-by-name-parameters.md index a85eb1b2e81e..fa850454d623 100644 --- a/docs/docs/reference/contextual/implicit-by-name-parameters.md +++ b/docs/docs/reference/contextual/implicit-by-name-parameters.md @@ -10,9 +10,9 @@ trait Codec[T] { def write(x: T): Unit } -delegate intCodec for Codec[Int] = ??? +given intCodec as Codec[Int] = ??? -delegate optionCodec[T] for Codec[Option[T]] given (ev: => Codec[T]) { +given optionCodec[T] as Codec[Option[T]] given (ev: => Codec[T]) { def write(xo: Option[T]) = xo match { case Some(x) => ev.write(x) case None => @@ -33,20 +33,20 @@ if this is necessary to prevent an otherwise diverging expansion. The precise steps for synthesizing an argument for an implicit by-name parameter of type `=> T` are as follows. - 1. Create a new delegate for type `T`: + 1. Create a new given instance of type `T`: ```scala - delegate lv for T = ??? + given lv as T = ??? ``` where `lv` is an arbitrary fresh name. - 1. This delegate is not immediately available as candidate for argument inference (making it immediately available could result in a loop in the synthesized computation). But it becomes available in all nested contexts that look again for an argument to an implicit by-name parameter. + 1. This given instance is not immediately available as candidate for argument inference (making it immediately available could result in a loop in the synthesized computation). But it becomes available in all nested contexts that look again for an argument to an implicit by-name parameter. - 1. If this search succeeds with expression `E`, and `E` contains references to the delegate `lv`, replace `E` by + 1. If this search succeeds with expression `E`, and `E` contains references to `lv`, replace `E` by ```scala - { delegate lv for T = E; lv } + { given lv as T = E; lv } ``` Otherwise, return `E` unchanged. @@ -55,10 +55,11 @@ In the example above, the definition of `s` would be expanded as follows. ```scala val s = the[Test.Codec[Option[Int]]]( - optionCodec[Int](intCodec)) + optionCodec[Int](intCodec) +) ``` -No local delegate was generated because the synthesized argument is not recursive. +No local given instance was generated because the synthesized argument is not recursive. ### Reference diff --git a/docs/docs/reference/contextual/implicit-function-types-spec.md b/docs/docs/reference/contextual/implicit-function-types-spec.md index f2f715c46821..0688e61580c3 100644 --- a/docs/docs/reference/contextual/implicit-function-types-spec.md +++ b/docs/docs/reference/contextual/implicit-function-types-spec.md @@ -29,7 +29,7 @@ Implicit function types erase to normal function types, so these classes are generated on the fly for typechecking, but not realized in actual code. Implicit function literals `given (x1: T1, ..., xn: Tn) => e` map -implicit parameters `xi` of types `Ti` to the result of evaluating expression `e`. +implicit parameters `xi` of types `Ti` to the result of evaluating the expression `e`. The scope of each implicit parameter `xi` is `e`. The parameters must have pairwise distinct names. If the expected type of the implicit function literal is of the form @@ -42,7 +42,7 @@ type of `e`. `T` must be equivalent to a type which does not refer to any of the implicit parameters `xi`. The implicit function literal is evaluated as the instance creation -expression: +expression ```scala new scala.ImplicitFunctionN[T1, ..., Tn, T] { def apply given (x1: T1, ..., xn: Tn): T = e diff --git a/docs/docs/reference/contextual/implicit-function-types.md b/docs/docs/reference/contextual/implicit-function-types.md index 416419a809a3..cde35abeaa35 100644 --- a/docs/docs/reference/contextual/implicit-function-types.md +++ b/docs/docs/reference/contextual/implicit-function-types.md @@ -5,13 +5,14 @@ title: "Implicit Function Types" _Implicit functions_ are functions with (only) implicit parameters. Their types are _implicit function types_. Here is an example of an implicit function type: + ```scala type Executable[T] = given ExecutionContext => T ``` An implicit function is applied to synthesized arguments, in the same way a method with a given clause is applied. For instance: ```scala - delegate ec for ExecutionContext = ... + given ec as ExecutionContext = ... def f(x: Int): Executable[Int] = ... @@ -26,7 +27,7 @@ implicit function literal, `E` is converted to an implicit function literal by r ``` where the names `x_1`, ..., `x_n` are arbitrary. This expansion is performed before the expression `E` is typechecked, which means that `x_1`, ..., `x_n` -are available as delegates in `E`. +are available as givens in `E`. Like their types, implicit function literals are written with a `given` prefix. They differ from normal function literals in two ways: @@ -39,7 +40,7 @@ For example, continuing with the previous definitions, g(22) // is expanded to g(given ev => 22) - g(f(2)) // is expanded to g(given ev => f(2) given ev) + g(f(2)) // is expanded to g(given ev => (f(2) given ev)) g(given ctx => f(22) given ctx) // is left as it is ``` @@ -82,13 +83,13 @@ with implicit function types as parameters to avoid the plumbing boilerplate that would otherwise be necessary. ```scala def table(init: given Table => Unit) = { - delegate t for Table + given t as Table init t } def row(init: given Row => Unit) given (t: Table) = { - delegate r for Row + given r as Row init t.add(r) } @@ -111,7 +112,7 @@ With that setup, the table construction code above compiles and expands to: ``` ### Example: Postconditions -As a larger example, here is a way to define constructs for checking arbitrary postconditions using an extension method `ensuring`so that the checked result can be referred to simply by `result`. The example combines opaque aliases, implicit function types, and extension methods to provide a zero-overhead abstraction. +As a larger example, here is a way to define constructs for checking arbitrary postconditions using an extension method `ensuring` so that the checked result can be referred to simply by `result`. The example combines opaque aliases, implicit function types, and extension methods to provide a zero-overhead abstraction. ```scala object PostConditions { @@ -119,7 +120,8 @@ object PostConditions { def result[T] given (r: WrappedResult[T]): T = r - def (x: T) ensuring [T] (condition: given WrappedResult[T] => Boolean): T = + def (x: T) + ensuring[T](condition: given WrappedResult[T] => Boolean): T = assert(condition) given x } import PostConditions.{ensuring, result} @@ -128,9 +130,9 @@ val s = List(1, 2, 3).sum.ensuring(result == 6) ``` **Explanations**: We use an implicit function type `given WrappedResult[T] => Boolean` as the type of the condition of `ensuring`. An argument to `ensuring` such as -`(result == 6)` will therefore have a delegate for type `WrappedResult[T]` in +`(result == 6)` will therefore have a given instance of type `WrappedResult[T]` in scope to pass along to the `result` method. `WrappedResult` is a fresh type, to make sure -that we do not get unwanted delegates in scope (this is good practice in all cases +that we do not get unwanted givens in scope (this is good practice in all cases where implicit parameters are involved). Since `WrappedResult` is an opaque type alias, its values need not be boxed, and since `ensuring` is added as an extension method, its argument does not need boxing either. Hence, the implementation of `ensuring` is as about as efficient diff --git a/docs/docs/reference/contextual/import-delegate.md b/docs/docs/reference/contextual/import-delegate.md index f58d3209cfc4..c9be556de9ef 100644 --- a/docs/docs/reference/contextual/import-delegate.md +++ b/docs/docs/reference/contextual/import-delegate.md @@ -1,73 +1,90 @@ --- layout: doc-page -title: "Delegate Imports" +title: "Given Imports" --- -A special form of import is used to import delegates. Example: +A special form of import is used to import given instances. Example: ```scala object A { class TC - delegate tc for TC - def f given TC = ??? + given tc as TC + def f where TC = ??? } object B { import A._ - import delegate A._ + import given A._ } ``` In the code above, the `import A._` clause of object `B` will import all members -of `A` _except_ the delegate `tc`. Conversely, the second import `import delegate A._` will import _only_ that delegate. +of `A` _except_ the given instance `tc`. Conversely, the second import `import given A._` will import _only_ that given instance. -Generally, a normal import clause brings all definitions except delegates into scope whereas a delegate import brings only delegates into scope. +Generally, a normal import clause brings definitions other than given instances into scope whereas a `given` import brings only given instances into scope. There are two main benefits arising from these rules: - - It is made clearer where delegates in scope are coming from. - In particular, it is not possible to hide imported delegates in a long list of regular imports. - - It enables importing all delegates - without importing anything else. This is particularly important since delegates + - It is made clearer where givens in scope are coming from. + In particular, it is not possible to hide imported givens in a long list of regular imports. + - It enables importing all givens + without importing anything else. This is particularly important since givens can be anonymous, so the usual recourse of using named imports is not practical. ### Importing By Type -Since delegates can be anonymous it is not always practical to import them by their name, and wildcard imports are typically used instead. By-type imports provide a more specific alternative to wildcard imports, which makes it clearer what is imported. Example: +Since givens can be anonymous it is not always practical to import them by their name, and wildcard imports are typically used instead. By-type imports provide a more specific alternative to wildcard imports, which makes it clearer what is imported. Example: ```scala -import delegate A.{for TC} +import given A.{_: TC} ``` -This imports any delegate in `A` that has a type which conforms tp `TC`. There can be several bounding types following a `for` and bounding types can contain wildcards. +This imports any given in `A` that has a type which conforms to `TC`. Importing givens of several types `T1,...,Tn` +is expressed by bounding with a union type. +``` +import given A.{_: T1 | ... | Tn} +``` +Importing all instances of a parameterized if expressed by wildcard arguments. For instance, assuming the object ```scala -object Delegates { - delegate intOrd for Ordering[Int] - delegate [T: Ordering] listOrd for Ordering[List[T]] - delegate ec for ExecutionContext = ... - delegate im for Monoid[Int] +object Instances { + given intOrd as Ordering[Int] + given [T: Ordering] listOrd as Ordering[List[T]] + given ec as ExecutionContext = ... + given im as Monoid[Int] } ``` the import ```scala -import delegate Delegates.{for Ordering[_], ExecutionContext} +import given Instances.{_: Ordering[?] | ExecutionContext} ``` -would import the `intOrd`, `listOrd`, and `ec` delegates but leave out the `im` delegate, since it fits none of the specified bounds. +would import the `intOrd`, `listOrd`, and `ec` instances but leave out the `im` instance, since it fits none of the specified bounds. By-type imports can be mixed with by-name imports. If both are present in an import clause, by-type imports come last. For instance, the import clause ```scala -import delegate Instances.{im, for Ordering[_]} +import given Instances.{im, _: Ordering[?]} +``` +would import `im`, `intOrd`, and `listOrd` but leave out `ec`. + +Bounded wildcard selectors also work for normal imports and exports. For instance, consider the following `enum` definition: +```scala +enum Color { + case Red, Green, Blue, Magenta + + def isPrimary(c: Color): Boolean = ... +} +export Color.{_: Color} ``` -would import `im`, `intOrd`, and `listOrd` but leave out `ec`. By-type imports cannot be mixed with a wildcard import in the same import clause. +The export clause makes all four three `Color` values available as unqualified constants, but +leaves the `isPrimary` method alone. ### Migration -The rules for delegate imports given above have the consequence that a library +The rules for imports given above have the consequence that a library would have to migrate in lockstep with all its users from old style implicits and -normal imports to delegate definitions and imports. +normal imports to given instances and imports. The following modifications avoid this hurdle to migration. - 1. A delegate import also brings old style implicits into scope. So, in Scala 3.0 - an old-style implicit definition can be brought into scope either by a normal import or by a delegate import. + 1. An "import given" also brings old style implicits into scope. So, in Scala 3.0 + an old-style implicit definition can be brought into scope either by a normal import or by an import given. 2. In Scala 3.1, old-style implicits accessed through a normal import will give a deprecation warning. @@ -75,6 +92,6 @@ The following modifications avoid this hurdle to migration. 3. In some version after 3.1, old-style implicits accessed through a normal import will give a compiler error. -These rules mean that library users can use `import delegate` to access old-style implicits in Scala 3.0, +These rules mean that library users can use `import given` to access old-style implicits in Scala 3.0, and will be gently nudged and then forced to do so in later versions. Libraries can then switch to representation clauses once their user base has migrated. diff --git a/docs/docs/reference/contextual/motivation.md b/docs/docs/reference/contextual/motivation.md index 491543df75b3..19c1e252617d 100644 --- a/docs/docs/reference/contextual/motivation.md +++ b/docs/docs/reference/contextual/motivation.md @@ -47,27 +47,27 @@ Existing Scala programmers by and large have gotten used to the status quo and s The following pages introduce a redesign of contextual abstractions in Scala. They introduce four fundamental changes: - 1. [Delegates](./delegates.html) are a new way to define basic terms that can be synthesized. They replace implicit definitions. The core principle of the proposal is that, rather than mixing the `implicit` modifier with a large number of features, we have a single way to define terms that can be synthesized for types. + 1. [Given Instances](./delegates.html) are a new way to define basic terms that can be synthesized. They replace implicit definitions. The core principle of the proposal is that, rather than mixing the `implicit` modifier with a large number of features, we have a single way to define terms that can be synthesized for types. 2. [Given Clauses](./given-clauses.html) are a new syntax for implicit _parameters_ and their _arguments_. Both are introduced with the same keyword, `given`. This unambiguously aligns parameters and arguments, solving a number of language warts. It also allows us to have several implicit parameter sections, and to have implicit parameters followed by normal ones. - 3. [Delegate Imports](./import-delegate.html) are a new class of imports that specifically import delegates and nothing else. Delegates _must be_ imported with `import delegate`, a plain import will no longer bring them into scope. + 3. [Given Imports](./import-delegate.html) are a new class of imports that specifically import given instances and nothing else. Given instances _must be_ imported with `import given`, a plain import will no longer bring them into scope. - 4. [Implicit Conversions](./conversions.html) are now expressed as delegates of a standard `Conversion` class. All other forms of implicit conversions will be phased out. + 4. [Implicit Conversions](./conversions.html) are now expressed as given instances of a standard `Conversion` class. All other forms of implicit conversions will be phased out. This section also contains pages describing other language features that are related to context abstraction. These are: - [Context Bounds](./context-bounds.html), which carry over unchanged. - [Extension Methods](./extension-methods.html) replace implicit classes in a way that integrates better with typeclasses. - [Implementing Typeclasses](./typeclasses.html) demonstrates how some common typeclasses can be implemented using the new constructs. - - [Typeclass Derivation](./derivation.html) introduces constructs to automatically derive typeclass delegates for ADTs. + - [Typeclass Derivation](./derivation.html) introduces constructs to automatically derive typeclass instances for ADTs. - [Multiversal Equality](./multiversal-equality.html) introduces a special typeclass to support type safe equality. - [Implicit Function Types](./implicit-function-types.html) provide a way to abstract over given clauses. - [Implicit By-Name Parameters](./implicit-by-name-parameters.html) are an essential tool to define recursive synthesized values without looping. - - [Relationship with Scala 2 Implicits](./relationship-implicits.html) discusses the relationship between old-style implicits and new-style delegates and given clauses and how to migrate from one to the other. + - [Relationship with Scala 2 Implicits](./relationship-implicits.html) discusses the relationship between old-style implicits and new-style givens and how to migrate from one to the other. -Overall, the new design achieves a better separation of term inference from the rest of the language: There is a single way to define delegates instead of a multitude of forms all taking an `implicit` modifier. There is a single way to introduce implicit parameters and arguments instead of conflating implicit with normal arguments. There is a separate way to import delegates that does not allow them to hide in a sea of normal imports. And there is a single way to define an implicit conversion which is clearly marked as such and does not require special syntax. +Overall, the new design achieves a better separation of term inference from the rest of the language: There is a single way to define given instances instead of a multitude of forms all taking an `implicit` modifier. There is a single way to introduce implicit parameters and arguments instead of conflating implicit with normal arguments. There is a separate way to import given instances that does not allow them to hide in a sea of normal imports. And there is a single way to define an implicit conversion which is clearly marked as such and does not require special syntax. This design thus avoids feature interactions and makes the language more consistent and orthogonal. It will make implicits easier to learn and harder to abuse. It will greatly improve the clarity of the 95% of Scala programs that use implicits. It has thus the potential to fulfil the promise of term inference in a principled way that is also accessible and friendly. diff --git a/docs/docs/reference/contextual/multiversal-equality.md b/docs/docs/reference/contextual/multiversal-equality.md index 5ccdca4cd36c..456cb9a9b03e 100644 --- a/docs/docs/reference/contextual/multiversal-equality.md +++ b/docs/docs/reference/contextual/multiversal-equality.md @@ -31,9 +31,9 @@ that derives `Eql`, e.g. ```scala class T derives Eql ``` -Alternatively, one can also provide an `Eql` delegate directly, like this: +Alternatively, one can also provide an `Eql` given instance directly, like this: ```scala -delegate for Eql[T, T] = Eql.derived +given as Eql[T, T] = Eql.derived ``` This definition effectively says that values of type `T` can (only) be compared to other values of type `T` when using `==` or `!=`. The definition @@ -54,30 +54,28 @@ object Eql { } ``` -One can have several `Eql` delegates for a type. For example, the four +One can have several `Eql` givens for a type. For example, the four definitions below make values of type `A` and type `B` comparable with each other, but not comparable to anything else: ```scala -delegate for Eql[A, A] = Eql.derived -delegate for Eql[B, B] = Eql.derived -delegate for Eql[A, B] = Eql.derived -delegate for Eql[B, A] = Eql.derived +given as Eql[A, A] = Eql.derived +given as Eql[B, B] = Eql.derived +given as Eql[A, B] = Eql.derived +given as Eql[B, A] = Eql.derived ``` -The `scala.Eql` object defines a number of `Eql` delegates that together +The `scala.Eql` object defines a number of `Eql` givens that together define a rule book for what standard types can be compared (more details below). There's also a "fallback" instance named `eqlAny` that allows comparisons -over all types that do not themselves have an `Eql` delegate. `eqlAny` is -defined as follows: +over all types that do not themselves have an `Eql` given. `eqlAny` is defined as follows: ```scala def eqlAny[L, R]: Eql[L, R] = Eql.derived ``` -Even though `eqlAny` is not declared a delegate, the compiler will still -construct an `eqlAny` instance as answer to an implicit search for the -type `Eql[L, R]`, unless `L` or `R` have `Eql` delegates +Even though `eqlAny` is not declared a given instance, the compiler will still construct an `eqlAny` instance as answer to an implicit search for the +type `Eql[L, R]`, unless `L` or `R` have `Eql` given instances defined on them, or the language feature `strictEquality` is enabled The primary motivation for having `eqlAny` is backwards compatibility, @@ -90,20 +88,20 @@ import scala.language.strictEquality ``` or with a command line option `-language:strictEquality`. -## Deriving Eql Delegates +## Deriving Eql Instances -Instead of defining `Eql` delegates directly, it is often more convenient to derive them. Example: +Instead of defining `Eql` instances directly, it is often more convenient to derive them. Example: ```scala class Box[T](x: T) derives Eql ``` By the usual rules if [typeclass derivation](./derivation.html), -this generates the following `Eql` delegate in the companion object of `Box`: +this generates the following `Eql` instance in the companion object of `Box`: ```scala -delegate [T, U] for Eql[Box[T], Box[U]] given Eql[T, U] = Eql.derived +given [T, U] as Eql[Box[T], Box[U]] given Eql[T, U] = Eql.derived ``` That is, two boxes are comparable with `==` or `!=` if their elements are. Examples: ```scala -new Box(1) == new Box(1L) // ok since there is a delegate for `Eql[Int, Long]` +new Box(1) == new Box(1L) // ok since there is an instance for `Eql[Int, Long]` new Box(1) == new Box("a") // error: can't compare new Box(1) == 1 // error: can't compare ``` @@ -116,7 +114,7 @@ If the `strictEquality` feature is enabled then a comparison using `x == y` or `x != y` between values `x: T` and `y: U` is legal if - 1. there is a delegate for `Eql[T, U]`, or + 1. there is a given instance for `Eql[T, U]`, or 2. one of `T`, `U` is `Null`. In the default case where the `strictEquality` feature is not enabled the comparison is @@ -124,24 +122,24 @@ also legal if 1. `T` and `U` the same, or 2. one of `T` and `U`is a subtype of the _lifted_ version of the other type, or - 3. neither `T` nor `U` have a _reflexive `Eql` delegate_. + 3. neither `T` nor `U` have a _reflexive `Eql` given. Explanations: - _lifting_ a type `S` means replacing all references to abstract types in covariant positions of `S` by their upper bound, and to replacing all refinement types in covariant positions of `S` by their parent. - - a type `T` has a _reflexive `Eql` delegate_ if the implicit search for `Eql[T, T]` + - a type `T` has a _reflexive `Eql` given if the implicit search for `Eql[T, T]` succeeds. -## Predefined Eql Delegates +## Predefined Eql Instances -The `Eql` object defines delegates for comparing +The `Eql` object defines givens for comparing - the primitive types `Byte`, `Short`, `Char`, `Int`, `Long`, `Float`, `Double`, `Boolean`, and `Unit`, - `java.lang.Number`, `java.lang.Boolean`, and `java.lang.Character`, - `scala.collection.Seq`, and `scala.collection.Set`. -Delegates are defined so that every one of these types has a reflexive `Eql` delegate, and the following holds: +Given instances are defined so that every one of these types has a reflexive `Eql` given, and the following holds: - Primitive numeric types can be compared with each other. - Primitive numeric types can be compared with subtypes of `java.lang.Number` (and _vice versa_). @@ -191,7 +189,7 @@ Unfortunately, the crucial ability to "lift" equality type checking from simple def contains[U >: T](x: U) given Eql1[U]: Boolean // (2) ``` This version could be applied just as widely as the original `contains(x: Any)` method, -since the `Eql1[Any]` fallback is always available! So we have gained nothing. What got lost in the transition to a single parameter type class was the original rule that `Eql[A, B]` is available only if neither `A` nor `B` have a reflexive `Eql` delegate. That rule simply cannot be expressed if there is a single type parameter for `Eql`. +since the `Eql1[Any]` fallback is always available! So we have gained nothing. What got lost in the transition to a single parameter type class was the original rule that `Eql[A, B]` is available only if neither `A` nor `B` have a reflexive `Eql` given. That rule simply cannot be expressed if there is a single type parameter for `Eql`. The situation is different under `-language:strictEquality`. In that case, the `Eql[Any, Any]` or `Eql1[Any]` instances would never be available, and the @@ -200,7 +198,7 @@ single and two-parameter versions would indeed coincide for most practical purpo But assuming `-language:strictEquality` immediately and everywhere poses migration problems which might well be unsurmountable. Consider again `contains`, which is in the standard library. Parameterizing it with the `Eql` type class as in (1) is an immediate win since it rules out non-sensical applications while still allowing all sensible ones. So it can be done almost at any time, modulo binary compatibility concerns. On the other hand, parameterizing `contains` with `Eql1` as in (2) would make `contains` -unusable for all types that have not yet declared an `Eql1` delegate, including all +unusable for all types that have not yet declared an `Eql1` given, including all types coming from Java. This is clearly unacceptable. It would lead to a situation where, rather than migrating existing libraries to use safe equality, the only upgrade path is to have parallel libraries, with the new version only catering to types deriving `Eql1` and the old version dealing with everything else. Such a split of the ecosystem would be very problematic, which means the cure is likely to be worse than the disease. diff --git a/docs/docs/reference/contextual/relationship-implicits.md b/docs/docs/reference/contextual/relationship-implicits.md index 1a33e5882efc..95b1f160ed0f 100644 --- a/docs/docs/reference/contextual/relationship-implicits.md +++ b/docs/docs/reference/contextual/relationship-implicits.md @@ -7,28 +7,28 @@ Many, but not all, of the new contextual abstraction features in Scala 3 can be ## Simulating Contextual Abstraction with Implicits -### Delegates +### Given Instances -Delegate clauses can be mapped to combinations of implicit objects, classes and implicit methods. +Given instances can be mapped to combinations of implicit objects, classes and implicit methods. - 1. Delegates without parameters are mapped to implicit objects. E.g., + 1. Given instances without parameters are mapped to implicit objects. E.g., ```scala - delegate IntOrd for Ord[Int] { ... } + given IntOrd as Ord[Int] { ... } ``` maps to ```scala implicit object IntOrd extends Ord[Int] { ... } ``` - 2. Parameterized delegates are mapped to combinations of classes and implicit methods. E.g., + 2. Parameterized given instances are mapped to combinations of classes and implicit methods. E.g., ```scala - delegate ListOrd[T] for Ord[List[T]] given (ord: Ord[T]) { ... } + given ListOrd[T] as Ord[List[T]] given (ord: Ord[T]) { ... } ``` maps to ```scala class ListOrd[T](implicit ord: Ord[T]) extends Ord[List[T]] { ... } final implicit def ListOrd[T](implicit ord: Ord[T]): ListOrd[T] = new ListOrd[T] ``` - 3. Alias delegates map to implicit methods. If an alias has neither type parameters nor a given clause, its right-hand side is cached in a variable. There are two cases that can be optimized: + 3. Alias givens map to implicit methods. If an alias has neither type parameters nor a given clause, its right-hand side is cached in a variable. There are two cases that can be optimized: - If the right hand side is a simple reference, we can use a forwarder to that reference without caching it. @@ -38,11 +38,11 @@ Delegate clauses can be mapped to combinations of implicit objects, classes and Examples: ```scala - delegate global for ExecutionContext = new ForkJoinContext() - delegate config for Config = default.config + given global as ExecutionContext = new ForkJoinContext() + given config as Config = default.config val ctx: Context - delegate for Context = ctx + given as Context = ctx ``` would map to ```scala @@ -54,31 +54,31 @@ Delegate clauses can be mapped to combinations of implicit objects, classes and final implicit val config: Config = default.config - final implicit def Context_delegate = ctx + final implicit def Context_given = ctx ``` -### Anonymous Delegates +### Anonymous Given Instances -Anonymous delegates get compiler synthesized names, which are generated in a reproducible way from the implemented type(s). For example, if the names of the `IntOrd` and `ListOrd` delegates above were left out, the following names would be synthesized instead: +Anonymous given instances get compiler synthesized names, which are generated in a reproducible way from the implemented type(s). For example, if the names of the `IntOrd` and `ListOrd` givens above were left out, the following names would be synthesized instead: ```scala - delegate Ord_Int_delegate for Ord[Int] { ... } - delegate Ord_List_delegate[T] for Ord[List[T]] { ... } + given Ord_Int_given as Ord[Int] { ... } + given Ord_List_given[T] as Ord[List[T]] { ... } ``` The synthesized type names are formed from - the simple name(s) of the implemented type(s), leaving out any prefixes, - the simple name(s) of the toplevel argument type constructors to these types - - the suffix `_delegate`. + - the suffix `_given`. -Anonymous delegates that define extension methods without also implementing a type +Anonymous given instances that define extension methods without also implementing a type get their name from the name of the first extension method and the toplevel type -constructor of its first parameter. For example, the delegate +constructor of its first parameter. For example, the given instance ```scala - delegate { - def (xs: List[T]) second[T] = ... + given { + def (xs: List[T]) second[T] = ... } ``` -gets the synthesized name `second_of_List_T_delegate`. +gets the synthesized name `second_of_List_T_given`. ### Given Clauses @@ -121,7 +121,7 @@ could be simulated to some degree by def circumference: Double = c.radius * math.Pi * 2 } ``` -Extension methods in delegates have no direct counterpart in Scala-2. The only way to simulate these is to make implicit classes available through imports. The Simulacrum macro library can automate this process in some cases. +Extension methods in given instances have no direct counterpart in Scala-2. The only way to simulate these is to make implicit classes available through imports. The Simulacrum macro library can automate this process in some cases. ### Typeclass Derivation @@ -139,25 +139,24 @@ Implicit by-name parameters are not supported in Scala 2, but can be emulated to ### Implicit Conversions -Implicit conversion methods in Scala 2 can be expressed as delegates -of the `scala.Conversion` class in Dotty. E.g. instead of +Implicit conversion methods in Scala 2 can be expressed as given instances of the `scala.Conversion` class in Dotty. E.g. instead of ```scala implicit def stringToToken(str: String): Token = new Keyword(str) ``` one can write ```scala - delegate stringToToken for Conversion[String, Token] { + given stringToToken as Conversion[String, Token] { def apply(str: String): Token = new KeyWord(str) } ``` ### Implicit Classes -Implicit classes in Scala 2 are often used to define extension methods, which are directly supported in Dotty. Other uses of implicit classes can be simulated by a pair of a regular class and a conversion delegate. +Implicit classes in Scala 2 are often used to define extension methods, which are directly supported in Dotty. Other uses of implicit classes can be simulated by a pair of a regular class and a given instance of `Conversion` type. ### Implicit Values -Implicit `val` definitions in Scala 2 can be expressed in Dotty using a regular `val` definition and an alias delegate. +Implicit `val` definitions in Scala 2 can be expressed in Dotty using a regular `val` definition and an alias given. E.g., Scala 2's ```scala lazy implicit val pos: Position = tree.sourcePos @@ -165,19 +164,19 @@ E.g., Scala 2's can be expressed in Dotty as ```scala lazy val pos: Position = tree.sourcePos - delegate for Position = pos + given as Position = pos ``` ### Abstract Implicits -An abstract implicit `val` or `def` in Scala 2 can be expressed in Dotty using a regular abstract definition and an alias delegate. E.g., Scala 2's +An abstract implicit `val` or `def` in Scala 2 can be expressed in Dotty using a regular abstract definition and an alias given. E.g., Scala 2's ```scala implicit def symDeco: SymDeco ``` can be expressed in Dotty as ```scala def symDeco: SymDeco - delegate for SymDeco = symDeco + given as SymDeco = symDeco ``` ## Implementation Status and Timeline diff --git a/docs/docs/reference/contextual/typeclasses.md b/docs/docs/reference/contextual/typeclasses.md index 95c1c18b15e6..435320bbe4f0 100644 --- a/docs/docs/reference/contextual/typeclasses.md +++ b/docs/docs/reference/contextual/typeclasses.md @@ -3,9 +3,9 @@ layout: doc-page title: "Implementing Typeclasses" --- -Delegates, extension methods and context bounds +Given instances, extension methods and context bounds allow a concise and natural expression of _typeclasses_. Typeclasses are just traits -with canonical implementations defined by delegates. Here are some examples of standard typeclasses: +with canonical implementations defined by given instances. Here are some examples of standard typeclasses: ### Semigroups and monoids: @@ -20,12 +20,12 @@ object Monoid { def apply[T] given Monoid[T] = the[Monoid[T]] } -delegate for Monoid[String] { +given as Monoid[String] { def (x: String) combine (y: String): String = x.concat(y) def unit: String = "" } -delegate for Monoid[Int] { +given as Monoid[Int] { def (x: Int) combine (y: Int): Int = x + y def unit: Int = 0 } @@ -48,14 +48,14 @@ trait Monad[F[_]] extends Functor[F] { def pure[A](x: A): F[A] } -delegate ListMonad for Monad[List] { +given ListMonad as Monad[List] { def (xs: List[A]) flatMap [A, B] (f: A => List[B]): List[B] = xs.flatMap(f) def pure[A](x: A): List[A] = List(x) } -delegate ReaderMonad[Ctx] for Monad[[X] =>> Ctx => X] { +given ReaderMonad[Ctx] as Monad[[X] =>> Ctx => X] { def (r: Ctx => A) flatMap [A, B] (f: A => Ctx => B): Ctx => B = ctx => f(r(ctx))(ctx) def pure[A](x: A): Ctx => A = diff --git a/tests/neg/i5978.scala b/tests/neg/i5978.scala index 0d1ee5616d74..ef126acaa9b0 100644 --- a/tests/neg/i5978.scala +++ b/tests/neg/i5978.scala @@ -5,10 +5,10 @@ opaque type Position[Buffer] = Int trait TokenParser[Token, R] object TextParser { - delegate TP for TokenParser[Char, Position[CharSequence]] {} + given TP as TokenParser[Char, Position[CharSequence]] {} - delegate FromCharToken for Conversion[Char, Position[CharSequence]] - given (T: TokenParser[Char, Position[CharSequence]]) = ??? + given FromCharToken as Conversion[Char, Position[CharSequence]] given + (T: TokenParser[Char, Position[CharSequence]]) = ??? } @@ -22,7 +22,7 @@ object Testcase { val co_x : Position[CharSequence] = 'x' // error { - delegate XXX for Conversion[Char, Position[CharSequence]] = co_i + given XXX as Conversion[Char, Position[CharSequence]] = co_i val co_y : Position[CharSequence] = 'x' } } diff --git a/tests/pos/i5978.scala b/tests/pos/i5978.scala index b82382f978cd..3ca39dd896fc 100644 --- a/tests/pos/i5978.scala +++ b/tests/pos/i5978.scala @@ -7,19 +7,18 @@ trait TokenParser[Token, R] package p1 { object TextParser { - delegate TP for TokenParser[Char, Position[CharSequence]] {} + given TP as TokenParser[Char, Position[CharSequence]] {} - def f given - TokenParser[Char, Position[CharSequence]] = ??? + def f + given TokenParser[Char, Position[CharSequence]] = ??? - delegate FromCharToken for Conversion[Char, Position[CharSequence]] - given (T: TokenParser[Char, Position[CharSequence]])= ??? + given FromCharToken as Conversion[Char, Position[CharSequence]] + given (T: TokenParser[Char, Position[CharSequence]]) = ??? } - object Testcase { def main(args: Array[String]): Unit = { - import delegate TextParser._ + import given TextParser._ import TextParser._ val tp_v: TokenParser[Char, Position[CharSequence]] = TextParser.TP @@ -28,7 +27,7 @@ package p1 { val co_x : Position[CharSequence] = 'x' { - delegate XXX for Conversion[Char, Position[CharSequence]] = co_i + given XXX as Conversion[Char, Position[CharSequence]] = co_i val co_y : Position[CharSequence] = 'x' } } @@ -45,7 +44,7 @@ package p2 { object Testcase { def main(args: Array[String]): Unit = { import TextParser._ - import delegate TextParser._ + import given TextParser._ val tp_v: TokenParser[Char, Position[CharSequence]] = TextParser.TP val tp_i = the[TokenParser[Char, Position[CharSequence]]] @@ -63,7 +62,7 @@ package p3 { object Testcase { def main(args: Array[String]): Unit = { - import delegate TextParser._ + import given TextParser._ import TextParser._ val co_i: Conversion[Char, Position[CharSequence]] = the[Conversion[Char, Position[CharSequence]]] @@ -71,14 +70,22 @@ package p3 { { val tp_v: TokenParser[Char, Position[CharSequence]] = TextParser.TP val tp_i = the[TokenParser[Char, Position[CharSequence]]] - delegate for Conversion[Char, Position[CharSequence]] = co_i + given as Conversion[Char, Position[CharSequence]] = co_i val co_x : Position[CharSequence] = 'x' { - delegate XXX for Conversion[Char, Position[CharSequence]] = co_i + given XXX as Conversion[Char, Position[CharSequence]] = co_i val co_y : Position[CharSequence] = 'x' } } } } +} +package p4 { + class TC + given A as TC + given B[X[_], Y] as TC + + given C as TC + given TC } \ No newline at end of file diff --git a/tests/pos/reference/delegates.scala b/tests/pos/reference/delegates.scala index a919c208a5fe..7f021253a31f 100644 --- a/tests/pos/reference/delegates.scala +++ b/tests/pos/reference/delegates.scala @@ -32,12 +32,12 @@ class Common { object Instances extends Common { - delegate IntOrd for Ord[Int] { + given IntOrd as Ord[Int] { def (x: Int) compareTo (y: Int) = if (x < y) -1 else if (x > y) +1 else 0 } - delegate ListOrd[T] for Ord[List[T]] given Ord[T] { + given ListOrd[T] as Ord[List[T]] given Ord[T] { def (xs: List[T]) compareTo (ys: List[T]): Int = (xs, ys) match { case (Nil, Nil) => 0 case (Nil, _) => -1 @@ -48,25 +48,25 @@ object Instances extends Common { } } - delegate StringOps { + given StringOps { def (xs: Seq[String]) longestStrings: Seq[String] = { val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) } } - delegate ListOps { + given ListOps { def (xs: List[T]) second[T] = xs.tail.head } - delegate ListMonad for Monad[List] { + given ListMonad as Monad[List] { def (xs: List[A]) flatMap[A, B] (f: A => List[B]): List[B] = xs.flatMap(f) def pure[A](x: A): List[A] = List(x) } - delegate ReaderMonad[Ctx] for Monad[[X] =>> Ctx => X] { + given ReaderMonad[Ctx] as Monad[[X] =>> Ctx => X] { def (r: Ctx => A) flatMap[A, B] (f: A => Ctx => B): Ctx => B = ctx => f(r(ctx))(ctx) def pure[A](x: A): Ctx => A = @@ -105,7 +105,7 @@ object Instances extends Common { def (sym: Symbol) name: String } def symDeco: SymDeco - delegate for SymDeco = symDeco + given as SymDeco = symDeco } object TastyImpl extends TastyAPI { type Symbol = String @@ -119,20 +119,20 @@ object Instances extends Common { class C given (ctx: Context) { def f() = { locally { - delegate for Context = this.ctx + given as Context = this.ctx println(the[Context].value) } locally { lazy val ctx1 = this.ctx - delegate for Context = ctx1 + given as Context = ctx1 println(the[Context].value) } locally { - delegate d[T] for D[T] + given d[T] as D[T] println(the[D[Int]]) } locally { - delegate for D[Int] given Context + given as D[Int] given Context println(the[D[Int]]) } } @@ -141,7 +141,7 @@ object Instances extends Common { class Token(str: String) object Token { - delegate StringToToken for Conversion[String, Token] { + given StringToToken as Conversion[String, Token] { def apply(str: String): Token = new Token(str) } } @@ -152,14 +152,14 @@ object Instances extends Common { object PostConditions { opaque type WrappedResult[T] = T - private delegate WrappedResult { + private given WrappedResult { def apply[T](x: T): WrappedResult[T] = x def (x: WrappedResult[T]) unwrap[T]: T = x } def result[T] given (wrapped: WrappedResult[T]): T = wrapped.unwrap - delegate { + given { def (x: T) ensuring[T] (condition: given WrappedResult[T] => Boolean): T = { assert(condition given WrappedResult(x)) x @@ -168,12 +168,12 @@ object PostConditions { } object AnonymousInstances extends Common { - delegate for Ord[Int] { + given as Ord[Int] { def (x: Int) compareTo (y: Int) = if (x < y) -1 else if (x > y) +1 else 0 } - delegate [T: Ord] for Ord[List[T]] { + given [T: Ord] as Ord[List[T]] { def (xs: List[T]) compareTo (ys: List[T]): Int = (xs, ys) match { case (Nil, Nil) => 0 case (Nil, _) => -1 @@ -184,22 +184,22 @@ object AnonymousInstances extends Common { } } - delegate { + given { def (xs: Seq[String]) longestStrings: Seq[String] = { val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) } } - delegate { + given { def (xs: List[T]) second[T] = xs.tail.head } - delegate [From, To] for Convertible[List[From], List[To]] given (c: Convertible[From, To]) { + given [From, To] as Convertible[List[From], List[To]] given (c: Convertible[From, To]) { def (x: List[From]) convert: List[To] = x.map(c.convert) } - delegate for Monoid[String] { + given as Monoid[String] { def (x: String) combine (y: String): String = x.concat(y) def unit: String = "" } @@ -226,13 +226,13 @@ object Implicits extends Common { } implicit def ListOrd[T: Ord]: Ord[List[T]] = new ListOrd[T] - class Convertible_List_List_instance[From, To](implicit c: Convertible[From, To]) + class Convertible_List_List_given[From, To](implicit c: Convertible[From, To]) extends Convertible[List[From], List[To]] { def (x: List[From]) convert: List[To] = x.map(c.convert) } - implicit def Convertible_List_List_instance[From, To](implicit c: Convertible[From, To]) + implicit def Convertible_List_List_given[From, To](implicit c: Convertible[From, To]) : Convertible[List[From], List[To]] = - new Convertible_List_List_instance[From, To] + new Convertible_List_List_given[From, To] def maximum[T](xs: List[T]) (implicit cmp: Ord[T]): T = @@ -249,7 +249,7 @@ object Implicits extends Common { object Test extends App { Instances.test() import PostConditions.result - import delegate PostConditions._ + import given PostConditions._ val s = List(1, 2, 3).sum s.ensuring(result == 6) } @@ -274,9 +274,9 @@ object Completions { // // CompletionArg.from(statusCode) - delegate fromString for Conversion[String, CompletionArg] = Error(_) - delegate fromFuture for Conversion[Future[HttpResponse], CompletionArg] = Response(_) - delegate fromStatusCode for Conversion[Future[StatusCode], CompletionArg] = Status(_) + given fromString as Conversion[String, CompletionArg] = Error(_) + given fromFuture as Conversion[Future[HttpResponse], CompletionArg] = Response(_) + given fromStatusCode as Conversion[Future[StatusCode], CompletionArg] = Status(_) } import CompletionArg._ diff --git a/tests/run/boundedImports.scala b/tests/run/boundedImports.scala new file mode 100644 index 000000000000..04e3196975f5 --- /dev/null +++ b/tests/run/boundedImports.scala @@ -0,0 +1,27 @@ +class C[X] + +object A extends C[String] { + + val a: A.type = this + val b: String = "" + val c: Integer = 10 + +} + +enum Color { + case Red, Green, Blue + + def identity(x: Any): Any = "" +} + +object Test extends App { + val c = 0 + + import A.{_: C[_] | String} + assert(a eq A) + assert(b == "") + assert(c == 0) + + import Color.{_: Color} + assert(identity("hello") == "hello") +} \ No newline at end of file