From c0905f6042b4eb6c94ccf8e4f59613df1fe29d7d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 27 Nov 2017 19:17:31 +0100 Subject: [PATCH 1/5] Update "changes to implicit resolution" docs section --- .../reference/changed/implicit-resolution.md | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/docs/docs/reference/changed/implicit-resolution.md b/docs/docs/reference/changed/implicit-resolution.md index 8fa6eca24709..47d4025b5e9f 100644 --- a/docs/docs/reference/changed/implicit-resolution.md +++ b/docs/docs/reference/changed/implicit-resolution.md @@ -41,4 +41,40 @@ affect implicits on the language level. more deeply than `i`. Previously, this would have resulted in an ambiguity error. + 4. The treatment of ambiguity errors has changed. If an ambiguity is encountered + in some recursive step of an implicit search, the ambiguity is propagated to the caller. + Example: Say you have the following definitions: + + class A + class B extends C + class C + implicit def a1: A + implicit def a2: A + implicit def b(implicit a: A): B + implicit def c: C + + and the query `implicitly[C]`. + + This query would now be classified as ambiguous. This makes sense, after all + there are two possible solutions, `b(a1)` and `b(a2)`, neither of which is better + than the other and both of which are better than the third solution, `c`. + By contrast, Scala 2 would have rejected the search for `A` as + ambiguous, and subsequently have classified the query `b(implictly[A])` as a normal fail, + which means that the alternative `c` would be chosen as solution! + + Scala 2's somewhat puzzling behavior wrt ambiguity has been exploited to implement the analogue + of a "negated" search in implicit resolution, where a query `Q1` fails if some other + query `Q2` succeeds and `Q1` succeeds if `Q2` fails. With the new cleaned up behavior these + techniques no longer work. But there is now a new special type `scala.implicits.Not` which + implements negation directly. For any query type `Q`: `Not[Q]` succeeds if and only if the + implicit search for `Q` fails. + + 5. The treatment of divergence errors has also changed. A divergent implicit is + treated as a normal failure, after which alternatives are still tried. This also makes + sense: Encountering a divergent implicit means that we assume that no finite + solution can be found on the given path, but another path can still be tried. By contrast + most (but not all) divergence errors in Scala 2 would terminate the implicit + search as a whole. + + [//]: # todo: expand with precise rules From 8111c09638d1b4f9e01c06c4a478200d280073ae Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 28 Nov 2017 18:07:33 +0100 Subject: [PATCH 2/5] Add reference docs for 0.5 --- .../reference/changed/implicit-resolution.md | 2 +- .../reference/dependent-function-types.md | 48 +++++++++++++++++++ docs/sidebar.yml | 2 + 3 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 docs/docs/reference/dependent-function-types.md diff --git a/docs/docs/reference/changed/implicit-resolution.md b/docs/docs/reference/changed/implicit-resolution.md index 47d4025b5e9f..5b1947065ae3 100644 --- a/docs/docs/reference/changed/implicit-resolution.md +++ b/docs/docs/reference/changed/implicit-resolution.md @@ -29,7 +29,7 @@ affect implicits on the language level. /*!*/ 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 + Consider for instance the following scenario: def f(implicit i: C) = { def g(implicit j: C) = { diff --git a/docs/docs/reference/dependent-function-types.md b/docs/docs/reference/dependent-function-types.md new file mode 100644 index 000000000000..cb3fc5f26b40 --- /dev/null +++ b/docs/docs/reference/dependent-function-types.md @@ -0,0 +1,48 @@ +--- +layout: doc-page +title: "Dependent Function Types" +--- + +A dependent function type describes functions where the result type may depend +on the function's parameter values. Example: + + class Keyed { type Key; key: Key } + + def extractKey(k: Keyed): k.Key = k.key // a dependent method + val extractor: (k: Keyed) => k.Key = extractKey // a dependent function value + +Scala already has _dependent methods_, i.e. methods where the result +type refers to some of the parameters of the method. Method +`extractKey` is an example. Its result type, `k.key` refers its +parameter `k` (we also say, `k.Key` _depends_ on `k`). But so far it +was not possible to turn such methods into function values, so thay +they can be passed as parameters to other functions, or returned as +results. Dependent methods could not be turned into functions simply +because there was no type that could describe them. + +In Dotty this is now made possible. The type of the `extractor` value above is + + (k: Keyed) => k.Key + +This type describes function values that take an argument `x` of type +`Keyed` and return a result of type `x.Key`. + +Recall that a normal function type `A => B` is represented as an +instance of the `Function1` trait (i.e. `Function1[A, B]`) and +analogously for functions with more parameters. Dependent functions +are also represented as instances of these traits, but they get an additional +refinement. In fact, the dependent function type above is just syntactic sugar for + + Function1[Keyed, Keyed#Key] { + def apply(k: Keyed): k.Key + } + +In general, a dependent functon type `(x1: K1, ..., xN: KN) => R` of arity `N` +translates to + + FunctionN[K1, ..., Kn, R'] { + def apply(x1: K1, ..., xN: KN): R + } + +where the result type parameter `R` is an upper approximation of the +true result type `R` that does not mention any of the parameters `k1, ..., kN`. diff --git a/docs/sidebar.yml b/docs/sidebar.yml index 35a807c25705..09228fc8d360 100644 --- a/docs/sidebar.yml +++ b/docs/sidebar.yml @@ -25,6 +25,8 @@ sidebar: url: docs/reference/type-lambdas.html - title: Implicit Function Types url: docs/reference/implicit-function-types.html + - title: Dependent Function Types + url: docs/reference/dependent-function-types.html - title: Phantom Types url: docs/reference/phantom-types.html - title: Literal Singleton Types From 9fde917bd76e26cd6dc4ab2502329b05d55f04ce Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 29 Nov 2017 08:57:18 +0100 Subject: [PATCH 3/5] Address reviewers comments --- .../reference/dependent-function-types.md | 40 ++++++++++--------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/docs/docs/reference/dependent-function-types.md b/docs/docs/reference/dependent-function-types.md index cb3fc5f26b40..e45ed0ee4227 100644 --- a/docs/docs/reference/dependent-function-types.md +++ b/docs/docs/reference/dependent-function-types.md @@ -1,31 +1,31 @@ --- -layout: doc-page -title: "Dependent Function Types" +layout: doc-page`% +title: "Dependent`% Function Types" --- A dependent function type describes functions where the result type may depend on the function's parameter values. Example: - class Keyed { type Key; key: Key } + class Entry { type Key; key: Key } - def extractKey(k: Keyed): k.Key = k.key // a dependent method - val extractor: (k: Keyed) => k.Key = extractKey // a dependent function value + def extractKey(e: Entry): e.Key = e.key // a dependent method + val extractor: (e: Entry) => e.Key = extractKey // a dependent function value Scala already has _dependent methods_, i.e. methods where the result type refers to some of the parameters of the method. Method -`extractKey` is an example. Its result type, `k.key` refers its -parameter `k` (we also say, `k.Key` _depends_ on `k`). But so far it -was not possible to turn such methods into function values, so thay +`extractKey` is an example. Its result type, `e.key` refers its +parameter `e` (we also say, `e.Key` _depends_ on `e`). But so far it +was not possible to turn such methods into function values, so that they can be passed as parameters to other functions, or returned as results. Dependent methods could not be turned into functions simply because there was no type that could describe them. -In Dotty this is now made possible. The type of the `extractor` value above is +In Dotty this is now possible. The type of the `extractor` value above is - (k: Keyed) => k.Key + (e: Entry) => e.Key -This type describes function values that take an argument `x` of type -`Keyed` and return a result of type `x.Key`. +This type describes function values that take any argument `x` of type +`Entry` and return a result of type `x.Key`. Recall that a normal function type `A => B` is represented as an instance of the `Function1` trait (i.e. `Function1[A, B]`) and @@ -33,16 +33,18 @@ analogously for functions with more parameters. Dependent functions are also represented as instances of these traits, but they get an additional refinement. In fact, the dependent function type above is just syntactic sugar for - Function1[Keyed, Keyed#Key] { - def apply(k: Keyed): k.Key + Function1[Entry, Entry#Key] { + def apply(e: Entry): e.Key } -In general, a dependent functon type `(x1: K1, ..., xN: KN) => R` of arity `N` +In general, a dependent functon type `(x1: K1, ..., xn: Kn) => R` of arity `n` translates to - FunctionN[K1, ..., Kn, R'] { - def apply(x1: K1, ..., xN: KN): R + Functionn[K1, ..., Kn, R'] { + def apply(x1: K1, ..., xn: Kn): R } -where the result type parameter `R` is an upper approximation of the -true result type `R` that does not mention any of the parameters `k1, ..., kN`. +where the result type parameter `R'` is an upper approximation of the +true result type `R` that does not mention any of the parameters `e1, ..., en`. + + From 06c5a1de31e26a6bb9432d3b600c0790f9a59b61 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 29 Nov 2017 08:59:43 +0100 Subject: [PATCH 4/5] Undo subscripts They do not work in code, which is where we would have needed them. --- docs/docs/reference/dependent-function-types.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/docs/reference/dependent-function-types.md b/docs/docs/reference/dependent-function-types.md index e45ed0ee4227..25f6223136c8 100644 --- a/docs/docs/reference/dependent-function-types.md +++ b/docs/docs/reference/dependent-function-types.md @@ -37,14 +37,14 @@ refinement. In fact, the dependent function type above is just syntactic sugar f def apply(e: Entry): e.Key } -In general, a dependent functon type `(x1: K1, ..., xn: Kn) => R` of arity `n` +In general, a dependent functon type `(x1: K1, ..., xN: KN) => R` of arity `N` translates to - Functionn[K1, ..., Kn, R'] { - def apply(x1: K1, ..., xn: Kn): R + FunctionN[K1, ..., Kn, R'] { + def apply(x1: K1, ..., xN: KN): R } where the result type parameter `R'` is an upper approximation of the -true result type `R` that does not mention any of the parameters `e1, ..., en`. +true result type `R` that does not mention any of the parameters `e1, ..., eN`. From e9204dd73a528f1ce9ef3a42a0b732bf63068b75 Mon Sep 17 00:00:00 2001 From: Allan Renucci Date: Fri, 1 Dec 2017 16:36:18 +0100 Subject: [PATCH 5/5] Polishing --- docs/docs/reference/changed/implicit-resolution.md | 14 +++++++------- docs/docs/reference/dependent-function-types.md | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/docs/reference/changed/implicit-resolution.md b/docs/docs/reference/changed/implicit-resolution.md index 5b1947065ae3..a30e166e15e6 100644 --- a/docs/docs/reference/changed/implicit-resolution.md +++ b/docs/docs/reference/changed/implicit-resolution.md @@ -59,15 +59,15 @@ affect implicits on the language level. there are two possible solutions, `b(a1)` and `b(a2)`, neither of which is better than the other and both of which are better than the third solution, `c`. By contrast, Scala 2 would have rejected the search for `A` as - ambiguous, and subsequently have classified the query `b(implictly[A])` as a normal fail, + ambiguous, and subsequently have classified the query `b(implicitly[A])` as a normal fail, which means that the alternative `c` would be chosen as solution! - Scala 2's somewhat puzzling behavior wrt ambiguity has been exploited to implement the analogue - of a "negated" search in implicit resolution, where a query `Q1` fails if some other - query `Q2` succeeds and `Q1` succeeds if `Q2` fails. With the new cleaned up behavior these - techniques no longer work. But there is now a new special type `scala.implicits.Not` which - implements negation directly. For any query type `Q`: `Not[Q]` succeeds if and only if the - implicit search for `Q` fails. + Scala 2's somewhat puzzling behavior with respect to ambiguity has been exploited to implement + the analogue of a "negated" search in implicit resolution, where a query `Q1` fails if some + other query `Q2` succeeds and `Q1` succeeds if `Q2` fails. With the new cleaned up behavior + these techniques no longer work. But there is now a new special type `scala.implicits.Not` + which implements negation directly. For any query type `Q`: `Not[Q]` succeeds if and only if + the implicit search for `Q` fails. 5. The treatment of divergence errors has also changed. A divergent implicit is treated as a normal failure, after which alternatives are still tried. This also makes diff --git a/docs/docs/reference/dependent-function-types.md b/docs/docs/reference/dependent-function-types.md index 25f6223136c8..e32885773019 100644 --- a/docs/docs/reference/dependent-function-types.md +++ b/docs/docs/reference/dependent-function-types.md @@ -1,6 +1,6 @@ --- -layout: doc-page`% -title: "Dependent`% Function Types" +layout: doc-page +title: "Dependent Function Types" --- A dependent function type describes functions where the result type may depend