-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Add docs on changed features #2592
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
d1c98fd
aa4217f
717b61c
9bb87f0
c3ea17d
331eb09
f14335b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 avid the performance overhead of thread synchonization because many lazy vals are only used in a single-threaded context. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. typo: avid -> avoid |
||
|
||
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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What exactly is the difference in behavior between volatile and non-volatile lazy vals? |
||
|
||
The [ScalaFix](https://scalacenter.github.io/scalafix/) rewrite tool | ||
as well as Dotty's own `-migration` option will rewrite all normal | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. -migration is only for migration warnings, to get dotty to rewrite stuff you need There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It might be best to not mention the dotty internal rewrite mechanism until we know what we want to do with it. |
||
lazy vals to volatile ones in order to keep their semantics compatible | ||
with current Scala's. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
dupport structural types fully. The reflction based implementation is | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Typo: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed, thanks! |
||
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. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
layout: doc-page | ||
title: "Changes in Type Checking" | ||
--- | ||
|
||
[//] # todo: fill in |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
--- | ||
layout: doc-page | ||
title: "Changes in Type Inference" | ||
--- | ||
|
||
[//] # todo: fill in | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is that by the way?