diff --git a/docs/docs/reference/other-new-features/control-syntax.md b/docs/docs/reference/other-new-features/control-syntax.md index 2f0bbde41331..d15632161de6 100644 --- a/docs/docs/reference/other-new-features/control-syntax.md +++ b/docs/docs/reference/other-new-features/control-syntax.md @@ -8,6 +8,16 @@ Scala 3 has a new "quiet" syntax for control expressions that does not rely on enclosing the condition in parentheses, and also allows to drop parentheses or braces around the generators of a `for`-expression. Examples: ```scala +//{ +import java.io.IOException +type T +var x: Int +var xs: List[Int] +var ys: List[Int] +var body: T +var handle: T +def f(x: Int): Int = ??? +//} if x < 0 then "negative" else if x == 0 then diff --git a/docs/docs/reference/other-new-features/creator-applications.md b/docs/docs/reference/other-new-features/creator-applications.md index 17bc1574f763..32b1f9226fcf 100644 --- a/docs/docs/reference/other-new-features/creator-applications.md +++ b/docs/docs/reference/other-new-features/creator-applications.md @@ -9,24 +9,26 @@ function application, without needing to write `new`. Scala 3 generalizes this scheme to all concrete classes. Example: -```scala -class StringBuilder(s: String): +```scala sc-name:Base.scala +class MyStringBuilder(s: String): def this() = this("") +``` -StringBuilder("abc") // old: new StringBuilder("abc") -StringBuilder() // old: new StringBuilder() +```scala sc-compile-with:Base.scala +MyStringBuilder("abc") // old: new MyStringBuilder("abc") +MyStringBuilder() // old: new MyStringBuilder() ``` This works since a companion object with two `apply` methods is generated together with the class. The object looks like this: -```scala -object StringBuilder: - inline def apply(s: String): StringBuilder = new StringBuilder(s) - inline def apply(): StringBuilder = new StringBuilder() +```scala sc-compile-with:Base.scala +object MyStringBuilder: + inline def apply(s: String): MyStringBuilder = new MyStringBuilder(s) + inline def apply(): MyStringBuilder = new MyStringBuilder() ``` -The synthetic object `StringBuilder` and its `apply` methods are called _constructor proxies_. +The synthetic object `MyStringBuilder` and its `apply` methods are called _constructor proxies_. Constructor proxies are generated even for Java classes and classes coming from Scala 2. The precise rules are as follows: diff --git a/docs/docs/reference/other-new-features/explicit-nulls.md b/docs/docs/reference/other-new-features/explicit-nulls.md index abcaaac58196..071daf381b43 100644 --- a/docs/docs/reference/other-new-features/explicit-nulls.md +++ b/docs/docs/reference/other-new-features/explicit-nulls.md @@ -22,6 +22,9 @@ val x: String | Null = null // ok A nullable type could have null value during runtime; hence, it is not safe to select a member without checking its nullity. ```scala +//{ +val x: String | Null +//} x.trim // error: trim is not member of String | Null ``` @@ -123,7 +126,7 @@ We illustrate the rules with following examples: - The first two rules are easy: we nullify reference types but not value types. ```java - class C { + abstract class C { String s; int x; } @@ -132,7 +135,7 @@ We illustrate the rules with following examples: ==> ```scala - class C: + abstract class C: val s: String | Null val x: Int ``` @@ -145,30 +148,33 @@ We illustrate the rules with following examples: ==> - ```scala - class C[T] { def foo(): T | Null } + ```scala sc-name:C.scala + class C[T] { def foo(): T | Null = null } ``` Notice this is rule is sometimes too conservative, as witnessed by - ```scala + ```scala sc:fail sc-compile-with:C.scala class InScala: - val c: C[Bool] = ??? // C as above - val b: Bool = c.foo() // no longer typechecks, since foo now returns Bool | Null + val c: C[Boolean] = ??? // C as above + val b: Boolean = c.foo() // no longer typechecks, since foo now returns Bool | Null ``` - We can reduce the number of redundant nullable types we need to add. Consider ```java - class Box { T get(); } - class BoxFactory { Box makeBox(); } + abstract class Box { T get(); } + abstract class BoxFactory { Box makeBox(); } ``` ==> - ```scala - class Box[T] { def get(): T | Null } - class BoxFactory[T] { def makeBox(): Box[T] | Null } + ```scala sc-name:Box.scala + abstract class Box[T] { def get(): T | Null } + ``` + + ```scala sc-compile-with:Box.scala + abstract class BoxFactory[T] { def makeBox(): Box[T] | Null } ``` Suppose we have a `BoxFactory[String]`. Notice that calling `makeBox()` on it returns a @@ -184,7 +190,7 @@ We illustrate the rules with following examples: - We will append `Null` to the type arguments if the generic class is defined in Scala. ```java - class BoxFactory { + abstract class BoxFactory { Box makeBox(); // Box is Scala-defined List>> makeCrazyBoxes(); // List is Java-defined } @@ -192,8 +198,8 @@ We illustrate the rules with following examples: ==> - ```scala - class BoxFactory[T]: + ```scala sc-compile-with:Box.scala + abstract class BoxFactory[T]: def makeBox(): Box[T | Null] | Null def makeCrazyBoxes(): java.util.List[Box[java.util.List[T] | Null]] | Null ``` @@ -221,10 +227,13 @@ We illustrate the rules with following examples: ==> ```scala + //{ + def getNewName(): String = ??? + //} class Constants: - val NAME: String("name") = "name" - val AGE: Int(0) = 0 - val CHAR: Char('a') = 'a' + val NAME: String = "name" + val AGE: Int = 0 + val CHAR: Char = 'a' val NAME_GENERATED: String | Null = getNewName() ``` @@ -242,8 +251,8 @@ We illustrate the rules with following examples: ==> - ```scala - class C: + ```scala sc-compile-with:Box.scala + abstract class C: val name: String def getNames(prefix: String | Null): java.util.List[String] // we still need to nullify the paramter types def getBoxedName(): Box[String | Null] // we don't append `Null` to the outmost level, but we still need to nullify inside @@ -252,7 +261,7 @@ We illustrate the rules with following examples: The annotation must be from the list below to be recognized as `NotNull` by the compiler. Check `Definitions.scala` for an updated list. - ```scala + ```scala sc:nocompile // A list of annotations that are commonly used to indicate // that a field/method argument or return type is not null. // These annotations are used by the nullification logic in @@ -283,11 +292,17 @@ Suppose we have Java method `String f(String x)`, we can override this method in ```scala def f(x: String | Null): String | Null +``` +```scala def f(x: String): String | Null +``` +```scala def f(x: String | Null): String +``` +```scala def f(x: String): String ``` @@ -304,7 +319,7 @@ Example: ```scala val s: String | Null = ??? -if s != null then +if s != null then ??? // s: String // s: String | Null @@ -316,9 +331,12 @@ assert(s != null) A similar inference can be made for the `else` case if the test is `p == null` ```scala +val s: String | Null = ??? if s == null then + ??? // s: String | Null else + ??? // s: String ``` @@ -332,13 +350,16 @@ We also support logical operators (`&&`, `||`, and `!`): val s: String | Null = ??? val s2: String | Null = ??? if s != null && s2 != null then + ??? // s: String // s2: String if s == null || s2 == null then + ??? // s: String | Null // s2: String | Null else + ??? // s: String // s2: String ``` @@ -351,11 +372,14 @@ We also support type specialization _within_ the condition, taking into account val s: String | Null = ??? if s != null && s.length > 0 then // s: String in `s.length > 0` + ??? // s: String if s == null || s.length > 0 then // s: String in `s.length > 0` + ??? // s: String | Null else + ??? // s: String ``` @@ -442,6 +466,7 @@ We don't support: val s: String | Null = ??? val s2: String | Null = ??? if s != null && s == s2 then + ??? // s: String inferred // s2: String not inferred ``` diff --git a/docs/docs/reference/other-new-features/export.md b/docs/docs/reference/other-new-features/export.md index a5918fdf93ca..5259365bcc21 100644 --- a/docs/docs/reference/other-new-features/export.md +++ b/docs/docs/reference/other-new-features/export.md @@ -6,7 +6,7 @@ movedTo: https://docs.scala-lang.org/scala3/reference/other-new-features/export. An export clause defines aliases for selected members of an object. Example: -```scala +```scala sc-name:Model.scala class BitMap class InkJet @@ -31,7 +31,7 @@ class Copier: The two `export` clauses define the following _export aliases_ in class `Copier`: -```scala +```scala sc:nocompile final def scan(): BitMap = scanUnit.scan() final def print(bits: BitMap): Unit = printUnit.print(bits) final type PrinterType = printUnit.PrinterType @@ -39,14 +39,14 @@ final type PrinterType = printUnit.PrinterType They can be accessed inside `Copier` as well as from outside: -```scala +```scala sc-compile-with:Model.scala val copier = new Copier copier.print(copier.scan()) ``` An export clause has the same format as an import clause. Its general form is: -```scala +```scala sc:nocompile export path . { sel_1, ..., sel_n } ``` @@ -86,9 +86,9 @@ referring to private values in the qualifier path are marked by the compiler as "stable" and their result types are the singleton types of the aliased definitions. This means that they can be used as parts of stable identifier paths, even though they are technically methods. For instance, the following is OK: ```scala class C { type T } -object O { val c: C = ... } +object O { val c: C = ??? } export O.c -def f: c.T = ... +def f: c.T = ??? ``` @@ -98,7 +98,7 @@ def f: c.T = ... 1. If an export clause contains a wildcard or given selector, it is forbidden for its qualifier path to refer to a package. This is because it is not yet known how to safely track wildcard dependencies to a package for the purposes of incremental compilation. 1. Simple renaming exports like - ```scala + ```scala sc:nocompile export status as stat ``` are not supported yet. They would run afoul of the restriction that the @@ -142,16 +142,19 @@ ImportSelectors ::= NamedSelector [‘,’ ImportSelectors] Export clauses raise questions about the order of elaboration during type checking. Consider the following example: -```scala -class B { val c: Int } +```scala sc-name:Base.scala +class B { val c: Int = ??? } object a { val b = new B } +``` + +```scala sc-compile-with:Base.scala export a.* export b.* ``` Is the `export b.*` clause legal? If yes, what does it export? Is it equivalent to `export a.b.*`? What about if we swap the last two clauses? -``` +```scala sc:fail sc-compile-with:Base.scala export b.* export a.* ``` diff --git a/docs/docs/reference/other-new-features/indentation-experimental.md b/docs/docs/reference/other-new-features/indentation-experimental.md index ed6e838fd5a4..45662b1d921f 100644 --- a/docs/docs/reference/other-new-features/indentation-experimental.md +++ b/docs/docs/reference/other-new-features/indentation-experimental.md @@ -18,6 +18,9 @@ This variant is more contentious and less stable than the rest of the significan Similar to what is done for classes and objects, a `:` that follows a function reference at the end of a line means braces can be omitted for function arguments. Example: ```scala +import language.experimental.fewerBraces +def times(i: Int)(fn: => Unit) = (0 to i).foreach(x => fn) + times(10): println("ah") println("ha") @@ -26,24 +29,37 @@ times(10): The colon can also follow an infix operator: ```scala -credentials ++ : - val file = Path.userHome / ".credentials" - if file.exists - then Seq(Credentials(file)) +import language.experimental.fewerBraces +//{ +val numbers: Seq[Int] +val n: Int +//} + +numbers ++ : + if n > 0 + then Seq(n) else Seq() ``` Function calls that take multiple argument lists can also be handled this way: ```scala +import language.experimental.fewerBraces +//{ +import java.io.File +import io.Source +val files: Map[String, File] +val fileName: String +//} + val firstLine = files.get(fileName).fold: - val fileNames = files.values + val fileNames = files.keys s"""no file named $fileName found among - |${values.mkString(\n)}""".stripMargin + |${fileNames.mkString("\n")}""".stripMargin : f => - val lines = f.iterator.map(_.readLine) - lines.mkString("\n) + val lines = Source.fromFile(f).getLines() + lines.mkString("\n") ``` @@ -51,10 +67,15 @@ val firstLine = files.get(fileName).fold: Braces can also be omitted around multiple line function value arguments: ```scala +import language.experimental.fewerBraces +//{ +val elems: Seq[Int] +//} + val xs = elems.map x => val y = x - 1 y * y -xs.foldLeft (x, y) => +xs.foldLeft(0) (x, y) => x + y ``` Braces can be omitted if the lambda starts with a parameter list and `=>` or `=>?` at the end of one line and it has an indented body on the following lines. @@ -73,10 +94,14 @@ IndentedArgument ::= indent (CaseClauses | Block) outdent Note that a lambda argument must have the `=>` at the end of a line for braces to be optional. For instance, the following would also be incorrect: -```scala +```scala sc-name:Base.scala +val xs: Seq[Int] +``` + +```scala sc:fail sc-compile-with:Base.scala xs.map x => x + 1 // error: braces or parentheses are required ``` The lambda has to be enclosed in braces or parentheses: -```scala +```scala sc-compile-with:Base.scala xs.map(x => x + 1) // ok ``` diff --git a/docs/docs/reference/other-new-features/indentation.md b/docs/docs/reference/other-new-features/indentation.md index c7a6fdbb7453..8d0f6b549dd7 100644 --- a/docs/docs/reference/other-new-features/indentation.md +++ b/docs/docs/reference/other-new-features/indentation.md @@ -21,7 +21,10 @@ The compiler enforces two rules for well-indented programs, flagging violations This rule is helpful for finding missing closing braces. It prevents errors like: - ```scala + ```scala sc:fail + //{ + val x: Int + //} if (x < 0) { println(1) println(2) @@ -32,6 +35,9 @@ The compiler enforces two rules for well-indented programs, flagging violations 2. If significant indentation is turned off (i.e. under Scala 2 mode or under `-no-indent`) and we are at the start of an indented sub-part of an expression, and the indented part ends in a newline, the next statement must start at an indentation width less than the sub-part. This prevents errors where an opening brace was forgotten, as in ```scala + //{ + val x: Int + //} if (x < 0) println(1) println(2) // error: missing `{` @@ -103,6 +109,9 @@ There are two rules: It is an error if the indentation width of the token following an `` does not match the indentation of some previous line in the enclosing indentation region. For instance, the following would be rejected. ```scala +//{ +val x: Int +//} if x < 0 then -x else // error: `else` does not align correctly @@ -114,14 +123,28 @@ at the top-level, inside braces `{...}`, but not inside parentheses `(...)`, pat **Note:** The rules for leading infix operators above are there to make sure that ```scala - one - + two.match - case 1 => b - case 2 => c - + three +//{ +val one: Int +val two: Int +val three: Int +val b: Int +val c: Int +//} +one ++ two.match + case 1 => b + case 2 => c ++ three ``` is parsed as `one + (two.match ...) + three`. Also, that ```scala +//{ +val x: Boolean +val a: Int +val b: Int +val c: Int +val d: Int +//} if x then a + b @@ -143,7 +166,7 @@ Analogous rules apply for enum bodies and local packages containing nested defin With these new rules, the following constructs are all valid: -```scala +```scala sc:nocompile trait A: def f: Int @@ -202,8 +225,10 @@ Indentation can be mixed freely with braces `{...}`, as well as brackets `[...]` For instance, consider: ```scala +def f(x: Int, fn: Int => Int) = ??? +val x: Int { - val x = f(x: Int, y => + val v = f(x, y => x * ( y + 1 ) + @@ -233,6 +258,7 @@ The indentation rules for `match` expressions and `catch` clauses are refined as The rules allow to write `match` expressions where cases are not indented themselves, as in the example below: ```scala +val x: Int x match case 1 => print("I") case 2 => print("II") @@ -250,20 +276,24 @@ Indentation-based syntax has many advantages over other conventions. But one pos To solve this problem, Scala 3 offers an optional `end` marker. Example: ```scala -def largeMethod(...) = - ... - if ... then ... +def largeMethod(/*...*/) = + /*...*/ + if ??? then + /*...*/ + ??? else - ... // a large block + /*...*/ + ??? end if - ... // more code + /*...*/ + ??? end largeMethod ``` An `end` marker consists of the identifier `end` and a follow-on specifier token that together constitute all the tokes of a line. Possible specifier tokens are identifiers or one of the following keywords -```scala +```scala sc:nocompile if while for match try new this val given ``` @@ -281,51 +311,47 @@ End markers are allowed in statement sequences. The specifier token `s` of an en For instance, the following end markers are all legal: ```scala -package p1.p2: - - abstract class C(): - - def this(x: Int) = - this() - if x > 0 then - val a :: b = - x :: Nil - end val - var y = - x - end y - while y > 0 do - println(y) - y -= 1 - end while - try - x match - case 0 => println("0") - case _ => - end match - finally - println("done") - end try - end if - end this - - def f: String - end C - - object C: - given C = - new C: - def f = "!" - end f - end new - end given - end C - - extension (x: C) - def ff: String = x.f ++ x.f - end extension - -end p2 +abstract class C(): + + def this(x: Int) = + this() + if x > 0 then + val a :: b = + x :: Nil + end val + var y = + x + end y + while y > 0 do + println(y) + y -= 1 + end while + try + x match + case 0 => println("0") + case _ => + end match + finally + println("done") + end try + end if + end this + + def f: String +end C + +object C: + given C = + new C: + def f = "!" + end f + end new + end given +end C + +extension (x: C) + def ff: String = x.f ++ x.f +end extension ``` #### When to Use End Markers @@ -423,6 +449,10 @@ import language.experimental.fewerBraces This variant is more contentious and less stable than the rest of the significant indentation scheme. In this variant, a colon `:` at the end of a line is also one of the possible tokens that opens an indentation region. Examples: ```scala +//{ +import language.experimental.fewerBraces +def times(i: Int)(fn: => Unit) = (0 to i).foreach(x => fn) +//} times(10): println("ah") println("ha") @@ -431,20 +461,27 @@ times(10): or ```scala -xs.map: - x => - val y = x - 1 - y * y +//{ +import language.experimental.fewerBraces +val elems: Seq[Int] +//} +val xs = elems.map x => + val y = x - 1 + y * y ``` The colon is usable not only for lambdas and by-name parameters, but also even for ordinary parameters: ```scala -credentials ++ : - val file = Path.userHome / ".credentials" - if file.exists - then Seq(Credentials(file)) +//{ +import language.experimental.fewerBraces +val numbers: Seq[Int] +val n: Int +//} +numbers ++ : + if n > 0 + then Seq(n) else Seq() ``` diff --git a/docs/docs/reference/other-new-features/kind-polymorphism.md b/docs/docs/reference/other-new-features/kind-polymorphism.md index 7dbcf3e96927..ff41c3f68cde 100644 --- a/docs/docs/reference/other-new-features/kind-polymorphism.md +++ b/docs/docs/reference/other-new-features/kind-polymorphism.md @@ -17,12 +17,15 @@ value that works for parameters of any kind. This is now possible through a form Kind polymorphism relies on the special type `scala.AnyKind` that can be used as an upper bound of a type. ```scala -def f[T <: AnyKind] = ... +def f[T <: AnyKind] = ??? ``` The actual type arguments of `f` can then be types of arbitrary kinds. So the following would all be legal: ```scala +//{ +def f[T <: AnyKind] = ??? +//} f[Int] f[List] f[Map] diff --git a/docs/docs/reference/other-new-features/matchable.md b/docs/docs/reference/other-new-features/matchable.md index fdce97dab8f0..75dc25cb3c1b 100644 --- a/docs/docs/reference/other-new-features/matchable.md +++ b/docs/docs/reference/other-new-features/matchable.md @@ -20,7 +20,7 @@ The `IArray` type offers extension methods for `length` and `apply`, but not for However, there is a potential hole due to pattern matching. Consider: ```scala -val imm: IArray[Int] = ... +val imm: IArray[Int] imm match case a: Array[Int] => a(0) = 1 ``` @@ -31,6 +31,7 @@ The test will succeed at runtime since `IArray`s _are_ represented as __Aside:__ One could also achieve the same by casting: ```scala +val imm: IArray[Int] imm.asInstanceOf[Array[Int]](0) = 1 ``` @@ -40,6 +41,7 @@ Note also that the problem is not tied to opaque types as match selectors. The f type `T` as match selector leads to the same problem: ```scala +val imm: IArray[Int] def f[T](x: T) = x match case a: Array[Int] => a(0) = 0 f(imm) @@ -75,7 +77,7 @@ extended by both `AnyVal` and `AnyRef`. Since `Matchable` is a supertype of ever Here is the hierarchy of top-level classes and traits with their defined methods: -```scala +```scala sc:nocompile abstract class Any: def getClass def isInstanceOf @@ -118,7 +120,7 @@ is guaranteed to succeed at run-time since `Any` and `Matchable` both erase to For instance, consider the definitions -```scala +```scala sc-name:Meter.scala opaque type Meter = Double def Meter(x: Double) = x @@ -128,14 +130,14 @@ def Second(x: Double) = x Here, universal `equals` will return true for -```scala - Meter(10).equals(Second(10)) +```scala sc-compile-with:Meter.scala +Meter(10).equals(Second(10)) ``` even though this is clearly false mathematically. With [multiversal equality](../contextual/multiversal-equality.md) one can mitigate that problem somewhat by turning -```scala - Meter(10) == Second(10) +```scala sc-compile-with:Meter.scala +Meter(10) == Second(10) ``` into a type error. diff --git a/docs/docs/reference/other-new-features/named-typeargs-spec.md b/docs/docs/reference/other-new-features/named-typeargs-spec.md index 404f96852aca..4b5fb8988617 100644 --- a/docs/docs/reference/other-new-features/named-typeargs-spec.md +++ b/docs/docs/reference/other-new-features/named-typeargs-spec.md @@ -16,7 +16,7 @@ NamedTypeArg ::= id ‘=’ Type Note in particular that named arguments cannot be passed to type constructors: -``` scala +``` scala sc:fail class C[T] val x: C[T = Int] = // error diff --git a/docs/docs/reference/other-new-features/named-typeargs.md b/docs/docs/reference/other-new-features/named-typeargs.md index e9a67b0368dd..10c3f9f8f95e 100644 --- a/docs/docs/reference/other-new-features/named-typeargs.md +++ b/docs/docs/reference/other-new-features/named-typeargs.md @@ -8,6 +8,10 @@ title: "Named Type Arguments" Type arguments of methods can now be specified by name as well as by position. Example: ``` scala +//{ +import scala.language.experimental.namedTypeArguments +// Currently, to use this feature you need to use experimental import +//} def construct[Elem, Coll[_]](xs: Elem*): Coll[Elem] = ??? val xs1 = construct[Coll = List, Elem = Int](1, 2, 3) diff --git a/docs/docs/reference/other-new-features/opaques-details.md b/docs/docs/reference/other-new-features/opaques-details.md index ecfb78db48b7..259ed842101d 100644 --- a/docs/docs/reference/other-new-features/opaques-details.md +++ b/docs/docs/reference/other-new-features/opaques-details.md @@ -21,6 +21,11 @@ at the top-level. They cannot be defined in local blocks. The general form of a (monomorphic) opaque type alias is ```scala +//{ +type L +type U +type R >: L <: U +//} opaque type T >: L <: U = R ``` @@ -29,11 +34,18 @@ where the lower bound `L` and the upper bound `U` may be missing, in which case Inside the scope of the alias definition, the alias is transparent: `T` is treated as a normal alias of `R`. Outside its scope, the alias is treated as the abstract type ```scala +//{ +type L +type U +//} type T >: L <: U ``` A special case arises if the opaque type alias is defined in an object. Example: ```scala +//{ +type R +//} object o: opaque type T = R ``` @@ -56,11 +68,18 @@ def id(x: o.T): o.T = x Opaque type aliases can have a single type parameter list. The following aliases are well-formed ```scala +//{ +type T +//} opaque type F[T] = (T, T) opaque type G = [T] =>> List[T] ``` but the following are not: -```scala +```scala sc:fail +//{ +type T +type U +//} opaque type BadF[T] = [U] =>> (T, U) opaque type BadG = [T] =>> [U] => (T, U) ``` @@ -74,7 +93,7 @@ defined on the underlying type. For instance, ```scala opaque type T = Int - ... + // ... val x: T val y: T x == y // uses Int equality for the comparison. @@ -96,7 +115,7 @@ object obj: def z: String = x // error: found: A, required: String ``` This behavior becomes clear if one recalls that top-level definitions are placed in their own synthetic object. For instance, the code in `test1.scala` would expand to -```scala +```scala sc:fail object test1$package: opaque type A = String val x: A = "abc" diff --git a/docs/docs/reference/other-new-features/opaques.md b/docs/docs/reference/other-new-features/opaques.md index 402440bad90c..1b98d3f1d969 100644 --- a/docs/docs/reference/other-new-features/opaques.md +++ b/docs/docs/reference/other-new-features/opaques.md @@ -6,7 +6,7 @@ movedTo: https://docs.scala-lang.org/scala3/reference/other-new-features/opaques Opaque types aliases provide type abstraction without any overhead. Example: -```scala +```scala sc-name:MyMath.scala object MyMath: opaque type Logarithm = Double @@ -41,7 +41,7 @@ The public API of `Logarithm` consists of the `apply` and `safe` methods defined They convert from `Double`s to `Logarithm` values. Moreover, an operation `toDouble` that converts the other way, and operations `+` and `*` are defined as extension methods on `Logarithm` values. The following operations would be valid because they use functionality implemented in the `MyMath` object. -```scala +```scala sc-compile-with:MyMath.scala import MyMath.Logarithm val l = Logarithm(1.0) @@ -52,7 +52,10 @@ val l4 = l + l2 But the following operations would lead to type errors: -```scala +```scala sc:fail sc-compile-with:MyMath.scala +import MyMath.Logarithm + +val l = Logarithm(1.0) val d: Double = l // error: found: Logarithm, required: Double val l2: Logarithm = 1.0 // error: found: Double, required: Logarithm l * 2 // error: found: Int(2), required: Logarithm @@ -63,7 +66,7 @@ l / l2 // error: `/` is not a member of Logarithm Opaque type aliases can also come with bounds. Example: -```scala +```scala sc-name:Access.scala object Access: opaque type Permissions = Int @@ -110,7 +113,7 @@ All three opaque type aliases have the same underlying representation type `Int` it known outside the `Access` object that `Permission` is a subtype of the other two types. Hence, the following usage scenario type-checks. -```scala +```scala sc-compile-with:Access.scala object User: import Access.* @@ -139,7 +142,7 @@ since `Permissions` and `PermissionChoice` are different, unrelated types outsid While typically, opaque types are used together with objects to hide implementation details of a module, they can also be used with classes. For example, we can redefine the above example of Logarithms as a class. -```scala +```scala sc-name:Logarithms.scala class Logarithms: opaque type Logarithm = Double @@ -153,7 +156,7 @@ class Logarithms: ``` Opaque type members of different instances are treated as different: -```scala +```scala sc:fail sc-compile-with:Logarithms.scala val l1 = new Logarithms val l2 = new Logarithms val x = l1(1.5) diff --git a/docs/docs/reference/other-new-features/open-classes.md b/docs/docs/reference/other-new-features/open-classes.md index f1ddcf8c190f..712cf2cae96b 100644 --- a/docs/docs/reference/other-new-features/open-classes.md +++ b/docs/docs/reference/other-new-features/open-classes.md @@ -7,8 +7,6 @@ movedTo: https://docs.scala-lang.org/scala3/reference/other-new-features/open-cl An `open` modifier on a class signals that the class is planned for extensions. Example: ```scala // File Writer.scala -package p - open class Writer[T]: /** Sends to stdout, can be overridden */ @@ -19,10 +17,11 @@ open class Writer[T]: end Writer // File EncryptedWriter.scala -package p +trait Encrypter[T]: + def encrypt(t: T): T -class EncryptedWriter[T: Encryptable] extends Writer[T]: - override def send(x: T) = super.send(encrypt(x)) +class EncryptedWriter[T: Encrypter] extends Writer[T]: + override def send(x: T) = super.send(summon[Encrypter[T]].encrypt(x)) ``` An open class typically comes with some documentation that describes the internal calling patterns between methods of the class as well as hooks that can be overridden. We call this the _extension contract_ of the class. It is different from the _external contract_ between a class and its users. diff --git a/docs/docs/reference/other-new-features/parameter-untupling-spec.md b/docs/docs/reference/other-new-features/parameter-untupling-spec.md index feedb77aa774..9b285d492921 100644 --- a/docs/docs/reference/other-new-features/parameter-untupling-spec.md +++ b/docs/docs/reference/other-new-features/parameter-untupling-spec.md @@ -5,24 +5,23 @@ movedTo: https://docs.scala-lang.org/scala3/reference/other-new-features/paramet --- ## Motivation - Say you have a list of pairs -```scala +```scala sc-name:Base.scala val xs: List[(Int, Int)] ``` and you want to map `xs` to a list of `Int`s so that each pair of numbers is mapped to their sum. Previously, the best way to do this was with a pattern-matching decomposition: -```scala +```scala sc-compile-with:Base.scala xs.map { case (x, y) => x + y } ``` While correct, this is inconvenient. Instead, we propose to write it the following way: -```scala +```scala sc-compile-with:Base.scala xs.map { (x, y) => x + y } @@ -30,7 +29,7 @@ xs.map { or, equivalently: -```scala +```scala sc-compile-with:Base.scala xs.map(_ + _) ``` @@ -60,18 +59,18 @@ Parameter untupling composes with eta-expansion. That is, an n-ary function gene If the function -```scala +```scala sc:nocompile (p1, ..., pn) => e ``` is feasible for parameter untupling with the expected type `TupleN[T1, ..., Tn] => Te`, then continue to type check the following adapted function ```scala -(x: TupleN[T1, ..., Tn]) => +def func[T1, T2 /*, Tn*/] = (x: Tuple2[T1, T2 /*, Tn*/]) => def p1: T1 = x._1 - ... - def pn: Tn = x._n - e + def p2: T2 = x._2 + // def pn: Tn = x._n + ??? ``` with the same expected type. diff --git a/docs/docs/reference/other-new-features/parameter-untupling.md b/docs/docs/reference/other-new-features/parameter-untupling.md index 1beed4968d4f..3bfe7a8e04ff 100644 --- a/docs/docs/reference/other-new-features/parameter-untupling.md +++ b/docs/docs/reference/other-new-features/parameter-untupling.md @@ -6,14 +6,14 @@ movedTo: https://docs.scala-lang.org/scala3/reference/other-new-features/paramet Say you have a list of pairs -```scala +```scala sc-name:Base.scala val xs: List[(Int, Int)] ``` and you want to map `xs` to a list of `Int`s so that each pair of numbers is mapped to their sum. Previously, the best way to do this was with a pattern-matching decomposition: -```scala +```scala sc-compile-with:Base.scala xs map { case (x, y) => x + y } @@ -22,7 +22,7 @@ xs map { While correct, this is also inconvenient and confusing, since the `case` suggests that the pattern match could fail. As a shorter and clearer alternative Scala 3 now allows -```scala +```scala sc-compile-with:Base.scala xs.map { (x, y) => x + y } @@ -30,7 +30,7 @@ xs.map { or, equivalently: -```scala +```scala sc-compile-with:Base.scala xs.map(_ + _) ``` diff --git a/docs/docs/reference/other-new-features/safe-initialization.md b/docs/docs/reference/other-new-features/safe-initialization.md index 09bb495f9263..45b391a28544 100644 --- a/docs/docs/reference/other-new-features/safe-initialization.md +++ b/docs/docs/reference/other-new-features/safe-initialization.md @@ -26,7 +26,7 @@ class RemoteFile(url: String) extends AbstractFile: The checker will report: -``` scala +```scala sc:nocompile -- Warning: tests/init/neg/AbstractFile.scala:7:4 ------------------------------ 7 | val localFile: String = s"${url.##}.tmp" // error: usage of `localFile` before it's initialized | ^ @@ -49,7 +49,7 @@ object Trees: The checker will report: -``` scala +``` scala sc:nocompile -- Warning: tests/init/neg/trees.scala:5:14 ------------------------------------ 5 | private var counter = 0 // error | ^ @@ -76,7 +76,7 @@ class Child extends Parent: The checker reports: -``` scala +``` scala sc:nocompile -- Warning: tests/init/neg/features-high-order.scala:7:6 ----------------------- 7 | val b = "hello" // error | ^ @@ -114,6 +114,9 @@ are initialized at the end of the primary constructor, except for the language feature below: ``` scala +//{ +type T +//} var x: T = _ ``` @@ -141,11 +144,19 @@ field points to an initialized object may not later point to an object under initialization. As an example, the following code will be rejected: ``` scala +//{ +class Context: + var reporter: Reporter = null + // ... +class File(s: String): + def write(s: String) = ??? + // ... +//} trait Reporter: def report(msg: String): Unit class FileReporter(ctx: Context) extends Reporter: - ctx.typer.reporter = this // ctx now reaches an uninitialized object + ctx.reporter = this // ctx now reaches an uninitialized object val file: File = new File("report.txt") def report(msg: String) = file.write(msg) ``` diff --git a/docs/docs/reference/other-new-features/targetName.md b/docs/docs/reference/other-new-features/targetName.md index a83c4247c1ef..b0682dd00b25 100644 --- a/docs/docs/reference/other-new-features/targetName.md +++ b/docs/docs/reference/other-new-features/targetName.md @@ -8,11 +8,12 @@ A `@targetName` annotation on a definition defines an alternate name for the imp ```scala import scala.annotation.targetName +trait Vec[T] object VecOps: extension [T](xs: Vec[T]) @targetName("append") - def ++= [T] (ys: Vec[T]): Vec[T] = ... + def ++= (ys: Vec[T]): Vec[T] = ??? ``` Here, the `++=` operation is implemented (in Byte code or native code) under the name `append`. The implementation name affects the code that is generated, and is the name under which code from other languages can call the method. For instance, `++=` could be invoked from Java like this: @@ -50,7 +51,7 @@ The `@targetName` annotation has no bearing on Scala usages. Any application of This means that `@targetName` annotations can be used to disambiguate two method definitions that would otherwise clash. For instance. -```scala +```scala sc:fail def f(x: => String): Int = x.length def f(x: => Int): Int = x + 1 // error: double definition ``` @@ -59,6 +60,7 @@ The two definitions above clash since their erased parameter types are both `Fun they have the same names and signatures. But we can avoid the clash by adding a `@targetName` annotation to either method or to both of them. Example: ```scala +import scala.annotation.targetName @targetName("f_string") def f(x: => String): Int = x.length def f(x: => Int): Int = x + 1 // OK @@ -69,7 +71,7 @@ This will produce methods `f_string` and `f` in the generated code. However, `@targetName` annotations are not allowed to break overriding relationships between two definitions that have otherwise the same names and types. So the following would be in error: -```scala +```scala sc:fail import annotation.targetName class A: def f(): Int = 1 @@ -77,17 +79,6 @@ class B extends A: @targetName("g") def f(): Int = 2 ``` -The compiler reports here: - -``` --- Error: test.scala:6:23 ------------------------------------------------------ -6 | @targetName("g") def f(): Int = 2 - | ^ - |error overriding method f in class A of type (): Int; - | method f of type (): Int should not have a @targetName - | annotation since the overridden member hasn't one either -``` - The relevant overriding rules can be summarized as follows: - Two members can override each other if their names and signatures are the same, @@ -97,7 +88,7 @@ The relevant overriding rules can be summarized as follows: As usual, any overriding relationship in the generated code must also be present in the original code. So the following example would also be in error: -```scala +```scala sc:fail import annotation.targetName class A: def f(): Int = 1 @@ -106,15 +97,4 @@ class B extends A: ``` Here, the original methods `g` and `f` do not override each other since they have -different names. But once we switch to target names, there is a clash that is reported by the compiler: - -``` --- [E120] Naming Error: test.scala:4:6 ----------------------------------------- -4 |class B extends A: - | ^ - | Name clash between defined and inherited member: - | def f(): Int in class A at line 3 and - | def g(): Int in class B at line 5 - | have the same name and type after erasure. -1 error found -``` +different names. But once we switch to target names, there is a clash that is reported by the compiler. diff --git a/docs/docs/reference/other-new-features/trait-parameters.md b/docs/docs/reference/other-new-features/trait-parameters.md index 9cc55c4e060b..e83016e656b6 100644 --- a/docs/docs/reference/other-new-features/trait-parameters.md +++ b/docs/docs/reference/other-new-features/trait-parameters.md @@ -6,7 +6,7 @@ movedTo: https://docs.scala-lang.org/scala3/reference/other-new-features/trait-p Scala 3 allows traits to have parameters, just like classes have parameters. -```scala +```scala sc-name:Greeting.scala trait Greeting(val name: String): def msg = s"How are you, $name" @@ -20,7 +20,7 @@ One potential issue with trait parameters is how to prevent ambiguities. For instance, you might try to extend `Greeting` twice, with different parameters. -```scala +```scala sc:fail sc-compile-with:Greeting.scala class D extends C, Greeting("Bill") // error: parameter passed twice ``` @@ -35,21 +35,21 @@ because it violates the second rule of the following for trait parameters: Here's a trait extending the parameterized trait `Greeting`. -```scala +```scala sc-compile-with:Greeting.scala sc-name:FormalGreeting.scala trait FormalGreeting extends Greeting: override def msg = s"How do you do, $name" ``` As is required, no arguments are passed to `Greeting`. However, this poses an issue when defining a class that extends `FormalGreeting`: -```scala +```scala sc:fail sc-compile-with:FormalGreeting.scala class E extends FormalGreeting // error: missing arguments for `Greeting`. ``` The correct way to write `E` is to extend both `Greeting` and `FormalGreeting` (in either order): -```scala +```scala sc-compile-with:FormalGreeting.scala class E extends Greeting("Bob"), FormalGreeting ``` @@ -75,7 +75,7 @@ class F(using iname: ImpliedName) extends ImpliedFormalGreeting ``` The definition of `F` in the last line is implicitly expanded to -```scala +```scala sc:nocompile class F(using iname: ImpliedName) extends Object, ImpliedGreeting(using iname), diff --git a/docs/docs/reference/other-new-features/transparent-traits.md b/docs/docs/reference/other-new-features/transparent-traits.md index 20141213db0a..e98293a945a0 100644 --- a/docs/docs/reference/other-new-features/transparent-traits.md +++ b/docs/docs/reference/other-new-features/transparent-traits.md @@ -12,6 +12,9 @@ Traits are used in two roles: Some traits are used primarily in the first role, and we usually do not want to see them in inferred types. An example is the `Product` trait that the compiler adds as a mixin trait to every case class or case object. In Scala 2, this parent trait sometimes makes inferred types more complicated than they should be. Example: ```scala +//{ +val condition: Boolean +//} trait Kind case object Var extends Kind case object Val extends Kind @@ -28,6 +31,9 @@ Here, the inferred type of `x` is `Set[Kind & Product & Serializable]` whereas o Scala 3 allows one to mark a mixin trait as `transparent`, which means that it can be suppressed in type inference. Here's an example that follows the lines of the code above, but now with a new transparent trait `S` instead of `Product`: ```scala +//{ +val condition: Boolean +//} transparent trait S trait Kind object Var extends Kind, S diff --git a/docs/docs/reference/other-new-features/type-test.md b/docs/docs/reference/other-new-features/type-test.md index da9fa6a1c2b3..a6ad7c396642 100644 --- a/docs/docs/reference/other-new-features/type-test.md +++ b/docs/docs/reference/other-new-features/type-test.md @@ -10,6 +10,11 @@ When pattern matching there are two situations where a runtime type test must be The first case is an explicit type test using the ascription pattern notation. ```scala +//{ +type X +type Y <: X +val x: X +//} (x: X) match case y: Y => ``` @@ -17,11 +22,16 @@ The first case is an explicit type test using the ascription pattern notation. The second case is when an extractor takes an argument that is not a subtype of the scrutinee type. ```scala +//{ +type X +type Y <: X +val x: X +//} (x: X) match case y @ Y(n) => object Y: - def unapply(x: Y): Some[Int] = ... + def unapply(x: Y): Some[Int] = ??? ``` In both cases, a class test will be performed at runtime. @@ -29,7 +39,7 @@ But when the type test is on an abstract type (type parameter or type member), t A `TypeTest` can be provided to make this test possible. -```scala +```scala sc:nocompile package scala.reflect trait TypeTest[-S, T]: @@ -40,7 +50,12 @@ It provides an extractor that returns its argument typed as a `T` if the argumen It can be used to encode a type test. ```scala -def f[X, Y](x: X)(using tt: TypeTest[X, Y]): Option[Y] = x match +import scala.reflect.TypeTest + +type Y +object Y: + def unapply(x: Y): Some[Int] = ??? +def f[X](x: X)(using tt: TypeTest[X, Y]): Option[Y] = x match case tt(x @ Y(1)) => Some(x) case tt(x) => Some(x) case _ => None @@ -51,7 +66,12 @@ This means that `x: Y` is transformed to `tt(x)` and `x @ Y(_)` to `tt(x @ Y(_)) The previous code is equivalent to ```scala -def f[X, Y](x: X)(using TypeTest[X, Y]): Option[Y] = x match +import scala.reflect.TypeTest + +type Y +object Y: + def unapply(x: Y): Some[Int] = ??? +def f[X](x: X)(using TypeTest[X, Y]): Option[Y] = x match case x @ Y(1) => Some(x) case x: Y => Some(x) case _ => None @@ -60,10 +80,15 @@ def f[X, Y](x: X)(using TypeTest[X, Y]): Option[Y] = x match We could create a type test at call site where the type test can be performed with runtime class tests directly as follows ```scala +import scala.reflect.TypeTest + +def f[X, Y](x: X)(using TypeTest[X, Y]): Option[Y] = x match + case x: Y => Some(x) + case _ => None val tt: TypeTest[Any, String] = new TypeTest[Any, String]: def unapply(s: Any): Option[s.type & String] = s match - case s: String => Some(s) + case s: (String & s.type) => Some(s) case _ => None f[AnyRef, String]("acb")(using tt) @@ -72,9 +97,15 @@ f[AnyRef, String]("acb")(using tt) The compiler will synthesize a new instance of a type test if none is found in scope as: ```scala +//{ +type A +type B +//} +import scala.reflect.TypeTest + new TypeTest[A, B]: def unapply(s: A): Option[s.type & B] = s match - case s: B => Some(s) + case s: (B & s.type) => Some(s) case _ => None ``` @@ -83,7 +114,7 @@ If the type tests cannot be done there will be an unchecked warning that will be The most common `TypeTest` instances are the ones that take any parameters (i.e. `TypeTest[Any, T]`). To make it possible to use such instances directly in context bounds we provide the alias -```scala +```scala sc:nocompile package scala.reflect type Typeable[T] = TypeTest[Any, T] @@ -92,6 +123,8 @@ type Typeable[T] = TypeTest[Any, T] This alias can be used as ```scala +import scala.reflect.Typeable + def f[T: Typeable]: Boolean = "abc" match case x: T => true @@ -112,10 +145,14 @@ Using `ClassTag` instances was unsound since classtags can check only the class ## Example Given the following abstract definition of Peano numbers that provides two given instances of types `TypeTest[Nat, Zero]` and `TypeTest[Nat, Succ]` +together with an implementation of Peano numbers based on type `Int` +it is possible to write the following program ```scala import scala.reflect.* +// Abstraction + trait Peano: type Nat type Zero <: Nat @@ -132,11 +169,9 @@ trait Peano: given typeTestOfZero: TypeTest[Nat, Zero] given typeTestOfSucc: TypeTest[Nat, Succ] -``` -together with an implementation of Peano numbers based on type `Int` +// Implementation based on type Int -```scala object PeanoInt extends Peano: type Nat = Int type Zero = Int @@ -157,12 +192,10 @@ object PeanoInt extends Peano: def typeTestOfSucc: TypeTest[Nat, Succ] = new: def unapply(x: Nat): Option[x.type & Succ] = if x > 0 then Some(x) else None -``` -it is possible to write the following program +// Program -```scala -@main def test = +def test = import PeanoInt.* def divOpt(m: Nat, n: Nat): Option[(Nat, Nat)] = diff --git a/docs/docs/usage/cbt-projects.md b/docs/docs/usage/cbt-projects.md index 59c9f07d8987..67a5e1bfedbb 100644 --- a/docs/docs/usage/cbt-projects.md +++ b/docs/docs/usage/cbt-projects.md @@ -9,7 +9,7 @@ incremental compilation is not supported), we recommend [using Dotty with sbt](s cbt comes with built-in Dotty support. Follow the [cbt tutorial](https://github.com/cvogt/cbt/), then simply extend `Dotty` in the Build class. -```scala +```scala sc:nocompile // build/build.scala import cbt.* class Build(val context: Context) extends Dotty { diff --git a/docs/docs/usage/getting-started.md b/docs/docs/usage/getting-started.md index 436183c391ec..f4fd41494b42 100644 --- a/docs/docs/usage/getting-started.md +++ b/docs/docs/usage/getting-started.md @@ -79,7 +79,7 @@ brew upgrade dotty ### Scala 3 for Scripting If you have followed the steps in "Standalone Installation" section and have the `scala` executable on your `PATH`, you can run `*.scala` files as scripts. Given a source named Test.scala: -```scala +```scala sc:nocompile @main def Test(name: String): Unit = println(s"Hello ${name}!") ``` diff --git a/docs/docs/usage/language-versions.md b/docs/docs/usage/language-versions.md index f40abf81a7b0..a1039fdf8939 100644 --- a/docs/docs/usage/language-versions.md +++ b/docs/docs/usage/language-versions.md @@ -24,11 +24,10 @@ There are two ways to specify a language version. - With a `-source` command line setting, e.g. `-source 3.0-migration`. - With a `scala.language` import at the top of a source file, e.g: -```scala -package p +```scala sc:nocompile import scala.language.`future-migration` -class C { ... } +class C { /*...*/ } ``` Language imports supersede command-line settings in the source files where they are specified. Only one language import specifying a source version is allowed in a source file, and it must come before any definitions in that file. diff --git a/docs/docs/usage/scaladoc/scaladocDocstrings.md b/docs/docs/usage/scaladoc/scaladocDocstrings.md index 115ef3395c80..8cc009d2c9ca 100644 --- a/docs/docs/usage/scaladoc/scaladocDocstrings.md +++ b/docs/docs/usage/scaladoc/scaladocDocstrings.md @@ -25,7 +25,7 @@ Scaladoc comments go before the items they pertain to in a special comment block * Even on empty paragraph-break lines. * * Note that the * on each line is aligned - * with the second * in /** so that the + * with the second * in /\** so that the * left margin is on the same column on the * first line and on subsequent ones. * @@ -83,7 +83,7 @@ These tags are well-suited to larger types or packages, with many members. They These tags are not enabled by default! You must pass the -groups flag to Scaladoc in order to turn them on. Typically the sbt for this will look something like: -```scala +```scala sc:nocompile Compile / doc / scalacOptions ++= Seq( "-groups" ) diff --git a/project/Build.scala b/project/Build.scala index 5a9d378dfd84..3a816a9d56f8 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -1371,7 +1371,8 @@ object Build { "-comment-syntax", "wiki", s"-source-links:docs=github://lampepfl/dotty/master#docs", "-doc-root-content", docRootFile.toString, - "-Ydocument-synthetic-types" + "-Ydocument-synthetic-types", + s"-snippet-compiler:docs/docs/usage=compile,docs/docs/release-notes=nocompile,docs/docs/reference/other-new-features=compile" ) ++ (if (justAPI) Nil else Seq("-siteroot", "docs", "-Yapi-subdirectory"))) if (dottyJars.isEmpty) Def.task { streams.value.log.error("Dotty lib wasn't found") }