diff --git a/docs/docs/reference/adts.md b/docs/docs/reference/adts.md index 9c4f83a6db93..486774e9997a 100644 --- a/docs/docs/reference/adts.md +++ b/docs/docs/reference/adts.md @@ -108,7 +108,7 @@ The changes are specified below as deltas with respect to the Scala syntax given EnumDef ::= id ClassConstr [`extends' [ConstrApps]] [nl] `{’ EnumCaseStat {semi EnumCaseStat} `}’ -2. Cases of enums are defined as follows: + 2. Cases of enums are defined as follows: EnumCaseStat ::= {Annotation [nl]} {Modifier} EnumCase EnumCase ::= `case' (EnumClassDef | ObjectDef | ids) diff --git a/docs/docs/reference/changed/implicit-conversions.md b/docs/docs/reference/changed/implicit-conversions.md new file mode 100644 index 000000000000..72a386eba468 --- /dev/null +++ b/docs/docs/reference/changed/implicit-conversions.md @@ -0,0 +1,48 @@ +--- +layout: doc-page +title: "Restrictions to Implicit Conversions" +--- + +Previously, an implicit value of type `Function1`, or any of its subtypes +could be used as an implicit conversion. That is, the following code would compile +even though it probably masks a type error: + + implicit val m: Map[String, Int] = Map(1 -> "abc") + + val x: String = 1 // scalac: assigns "abc" to x + // Dotty: type error + +By contrast, Dotty only considers _methods_ as implicit conversions, so the +`Map` value `m` above would not qualify as a conversion from `String` to `Int`. + +To be able to express implicit conversions passed as parameters, `Dotty` +introduces a new type + + abstract class ImplicitConverter[-T, +U] extends Function1[T, U] + +Implicit values of type `ImplicitConverter[A, B]` do qualify as implicit +conversions. It is as if there was a global implicit conversion method + + def convert[A, B](x: A)(implicit converter: ImplicitConverter[A, B]): B = + converter(x) + +(In reality the Dotty compiler simulates the behavior of this method directly in +its type checking because this turns out to be more efficient). + +In summary, previous code using implicit conversion parameters such as + + def useConversion(implicit f: A => B) { + val y: A = ... + val x: B = a // error under Dotty + } + +is no longer legal and has to be rwritten to + + def useConversion(implicit f: ImplicitConverter[A, B]) { + val y: A = ... + val x: B = a // OK + } + + + + diff --git a/docs/docs/reference/changed/implicit-resolution.md b/docs/docs/reference/changed/implicit-resolution.md new file mode 100644 index 000000000000..d0728043c251 --- /dev/null +++ b/docs/docs/reference/changed/implicit-resolution.md @@ -0,0 +1,46 @@ +--- +layout: doc-page +title: "Changes in Implicit Resolution" +--- + +Implicit resolution uses a new algorithm which caches implicit results +more aggressively for perforance. There are also some changes that +affect implicits on the language level. + + 1. Types of implicit values and result types of implicit methods + must be explicitly declared. Excepted are only values in local blocks + where the type may still be inferred: + + class C { + + val ctx: Context = ... // ok + + /*!*/ implicit val x = ... // error: type must be given explicitly + + /*!*/ next(): Context = ... // error: type must be given explicitly + + val y = { + implicit val ctx = this.ctx // ok + ... + } + + 2. Implicit parameters may not have singleton types. + + /*!*/ def f(implicit x: y.type) // error `y.type` not allowed as type of implicit + + 3. Nesting is now taken into account for selecting an implicit. + Consider for instance the following scenario + + def f(implicit i: C) = { + def g(implicit j: C) = { + implicitly[C] + } + } + + This will now resolve the `implicitly` call to `j`, because `j` is nested + more deeply than `i`. Previously, this would have resulted in an + ambiguity error. + +[//] # todo: expand with precise rules + + diff --git a/docs/docs/reference/changed/lazy-vals.md b/docs/docs/reference/changed/lazy-vals.md new file mode 100644 index 000000000000..49228d97c429 --- /dev/null +++ b/docs/docs/reference/changed/lazy-vals.md @@ -0,0 +1,19 @@ +--- +layout: doc-page +title: Changed: Lazy Vals and @volatile +--- + +Lazy val initialization no longer guarantees safe publishing. This change was done +to avoid the performance overhead of thread synchonization because many lazy vals are only used in a single-threaded context. + +To get back safe publishing you need to annotate a lazy val with `@volatile`. In + + @volatile lazy val x = expr + +it is guaranteed that readers of a lazy val will see the value of `x` +once it is assigned. + +The [ScalaFix](https://scalacenter.github.io/scalafix/) rewrite tool +as well as Dotty's own `-language:Scala2 -rewrite` option will rewrite all normal +lazy vals to volatile ones in order to keep their semantics compatible +with current Scala's. diff --git a/docs/docs/reference/changed/structural-types.md b/docs/docs/reference/changed/structural-types.md new file mode 100644 index 000000000000..95124d030743 --- /dev/null +++ b/docs/docs/reference/changed/structural-types.md @@ -0,0 +1,129 @@ +--- +layout: doc-page +title: "Programmatic Structural Types" +--- + +Previously, Scala supported structural types by means of +reflection. This is problematic on other platforms, because Scala's +reflection is JVM-based. Consequently, Scala.js and Scala.native don't +support structural types fully. The reflction based implementation is +also needlessly restrictive, since it rules out other implementation +schemes. This makes structural types unsuitable for e.g. modelling +rows in a database, for which they would otherwise seem to be an ideal +match. + +Dotty allows to implement structural types programmatically, using +"Selectables". `Selectable` is a trait defined as follows: + + trait Selectable extends Any { + def selectDynamic(name: String): Any + def selectDynamicMethod(name: String, paramClasses: ClassTag[_]*): Any = + new UnsupportedOperationException("selectDynamicMethod") + } + +The most important method of a `Selectable` is `selectDynamic`: It +takes a field name and returns the value associated with that name in +the selectable. + +Assume now `r` is a value with structural type `S`.` In general `S` is +of the form `C { Rs }`, i.e. it consists of a class reference `C` and +refinement declarations `Rs`. We call a field selection `r.f` +_structural_ if `f` is a name defined by a declaration in `Rs` whereas +`C` defines no member of name `f`. Assuming the selection has type +`T`, it is mapped to something equivalent to the following code: + + (r: Selectable).selectDynamic("f").asInstanceOf[T] + +That is, we make sure `r` conforms to type `Selectable`, potentially +by adding an implicit conversion. We then invoke the `get` operation +of that instance, passing the the name `"f"` as a parameter. We +finally cast the resulting value back to the statically known type +`T`. + +`Selectable` also defines another access method called +`selectDynamicMethod`. This operation is used to select methods +instead of fields. It gets passed the class tags of the selected +method's formal parameter types as additional arguments. These can +then be used to disambiguate one of several overloaded variants. + +Package `scala.reflect` contains an implicit conversion which can map +any value to a selectable that emulates reflection-based selection, in +a way similar to what was done until now: + + package scala.reflect + + object Selectable { + implicit def reflectiveSelectable(receiver: Any): scala.Selectable = + receiver match { + case receiver: scala.Selectable => receiver + case _ => new scala.reflect.Selectable(receiver) + } + } + +When imported, `reflectiveSelectable` provides a way to access fields +of any structural type using Java reflection. This is similar to the +current implementation of structural types. The main difference is +that to get reflection-based structural access one now has to add an +import: + + import scala.relect.Selectable.reflectiveSelectable + +On the other hand, the previously required language feature import of +`reflectiveCalls` is now redundant and is therefore dropped. + +As you can see from its implementation above, `reflectSelectable` +checks first whether its argument is already a run-time instance of +`Selectable`, in which case it is returned directly. This means that +reflection-based accesses only take place as a last resort, if no +other `Selectable` is defined. + +Other selectable instances can be defined in libraries. For instance, +here is a simple class of records that support dynamic selection: + + case class Record(elems: (String, Any)*) extends Selectable { + def selectDynamic(name: String): Any = elems.find(_._1 == name).get._2 + } + +`Record` consists of a list of pairs of element names and values. Its +`selectDynamic` operation finds the pair with given name and returns +its value. + +For illustration, let's define a record value and cast it to a +structural type `Person`: + + type Person = Record { val name: String; val age: Int } + val person = Record(("name" -> "Emma", "age" -> 42)).asInstanceOf[Person] + +Then `person.name` will have static type `String`, and will produce `"Emma"` as result. + +The safety of this scheme relies on the correctness of the cast. If +the cast lies about the structure of the record, the corresponding +`selectDynamic` operation would fail. In practice, the cast would +likely be part if a database access layer which would ensure its +correctness. + +## Notes: + +1. The scheme does not handle polymorphic methods in structural +refinements. Such polymorphic methods are currently flagged as +errors. It's not clear whether the use case is common enough to +warrant the additional complexity of supporting it. + +2. There are clearly some connections with `scala.Dynamic` here, since +both select members programmatically. But there are also some +differences. + + - Fully dynamic selection is not typesafe, but structural selection + is, as long as the correspondence of the structural type with the + underlying value is as stated. + + - `Dynamic` is just a marker trait, which gives more leeway where and + how to define reflective access operations. By contrast + `Selectable` is a trait which declares the access operations. + + - One access operation, `selectDynamic` is shared between both + approaches, but the other access operations are + different. `Selectable` defines a `selectDynamicMethod`, which + takes class tags indicating the method's formal parameter types as + additional argument. `Dynamic` comes with `applyDynamic` and + `updateDynamic` methods, which take actual argument values. diff --git a/docs/docs/reference/changed/type-checking.md b/docs/docs/reference/changed/type-checking.md new file mode 100644 index 000000000000..607f5ae8220c --- /dev/null +++ b/docs/docs/reference/changed/type-checking.md @@ -0,0 +1,6 @@ +--- +layout: doc-page +title: "Changes in Type Checking" +--- + +[//] # todo: fill in diff --git a/docs/docs/reference/changed/type-inference.md b/docs/docs/reference/changed/type-inference.md new file mode 100644 index 000000000000..276a5100dc19 --- /dev/null +++ b/docs/docs/reference/changed/type-inference.md @@ -0,0 +1,7 @@ +--- +layout: doc-page +title: "Changes in Type Inference" +--- + +[//] # todo: fill in + diff --git a/docs/docs/reference/changed/vararg-patterns.md b/docs/docs/reference/changed/vararg-patterns.md new file mode 100644 index 000000000000..68e8d67d3964 --- /dev/null +++ b/docs/docs/reference/changed/vararg-patterns.md @@ -0,0 +1,21 @@ +--- +layout: doc-page +title: "Vararg Patterns" +--- + +The syntax of vararg patterns has changed. In the new syntax one +writes varargs in patterns exactly like one writes them in +expressions, using a `: _*` type annotation: + + xs match { + case List(1, 2, xs: _*) => println(xs) // binds xs + case List(1, _ : _*) => // wildcard pattern + } + +The old syntax, which is shorter but less regular, is no longer +supported: + + /*!*/ case List(1, 2, xs @ _*) // syntax error + /*!*/ case List(1, 2, _*) => ... // syntax error + + diff --git a/docs/sidebar.yml b/docs/sidebar.yml index bec8c2b95ee7..a75fd36d5eca 100644 --- a/docs/sidebar.yml +++ b/docs/sidebar.yml @@ -5,14 +5,14 @@ sidebar: subsection: - title: New Types subsection: - - title: Implicit Function Types - url: docs/reference/implicit-function-types.md - title: Intersection types url: docs/reference/intersection-types.html - title: Union types url: docs/reference/union-types.html - title: Type lambdas url: docs/reference/type-lambdas.html + - title: Implicit Function Types + url: docs/reference/implicit-function-types.md - title: Enums subsection: - title: Enumerations @@ -21,20 +21,36 @@ sidebar: url: docs/reference/adts.html - title: Translation url: docs/reference/desugarEnums.html - - title: Trait Parameters - url: docs/reference/trait-parameters.html - - title: Multiversal Equality - url: docs/reference/multiversal-equality.html - - title: Inline - url: docs/reference/inline.html - - title: Smaller Changes + - title: Other New Features subsection: + - title: Multiversal Equality + url: docs/reference/multiversal-equality.html + - title: Inline + url: docs/reference/inline.html + - title: Trait Parameters + url: docs/reference/trait-parameters.html - title: By-Name Implicits url: docs/reference/implicit-by-name-parameters.html - title: Auto Parameter Tupling url: docs/reference/auto-parameter-tupling.html - title: Named Type Arguments url: docs/reference/named-typeargs.html + - title: Changed Features + subsection: + - title: Volatile Lazy Vals + url: docs/reference/changed/lazy-vals.md + - title: Structural Types + url: docs/reference/changed/structural-types.md + - title: Type Checking + url: docs/reference/changed/type-checking.md + - title: Type Inference + url: docs/reference/changed/type-inference.md + - title: Implicit Resolution + url: docs/reference/changed/implicit-resolution.md + - title: Implicit Conversions + url: docs/reference/changed/implicit-conversions.md + - title: Vararg Patterns + url: docs/reference/changed/vararg-patterns.md - title: Dropped Features subsection: - title: DelayedInit diff --git a/tests/pos/reference/vararg-patterns.scala b/tests/pos/reference/vararg-patterns.scala new file mode 100644 index 000000000000..3a2cf96a7175 --- /dev/null +++ b/tests/pos/reference/vararg-patterns.scala @@ -0,0 +1,6 @@ +package varargPatterns +object t1 extends App { + val List(1, 2, xs : _*) = List(1, 2, 3) + println(xs) + val List(1, 2, _ : _*) = List(1, 2, 3) +}