You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/docs/internals/explicit-nulls.md
+1-1Lines changed: 1 addition & 1 deletion
Original file line number
Diff line number
Diff line change
@@ -99,7 +99,7 @@ During adapting, if the type of the tree is not a subtype of the expected type,
99
99
2. If the `tree.tpe` is not nullable or the last step fails, we search on the tree directly.
100
100
3. If the last step fails, we try to cast tree to `pt` if the two types `isUnsafeConvertable`.
101
101
102
-
Since implicit search (find candidates and try to type the new tree) could run in some different contexts, we have to pass the `UnsafeNullConversion` mode to the search context.
102
+
Since implicit search (finding candidates and trying to type the new tree) could run in some different contexts, we have to pass the `UnsafeNullConversion` mode to the search context.
103
103
104
104
The SAM type conversion also happens in `adaptToSubType`. We need to strip `Null` from `pt` in order to get class information.
Copy file name to clipboardExpand all lines: docs/docs/reference/other-new-features/explicit-nulls.md
+75-68Lines changed: 75 additions & 68 deletions
Original file line number
Diff line number
Diff line change
@@ -7,14 +7,19 @@ Explicit nulls is an opt-in feature that modifies the Scala type system, which m
7
7
(anything that extends `AnyRef`) _non-nullable_.
8
8
9
9
This means the following code will no longer typecheck:
10
-
```
11
-
val x: String = null // error: found `Null`, but required `String`
10
+
```scala
11
+
valx:String=null// error: found `Null`, but required `String`
12
12
```
13
13
14
14
Instead, to mark a type as nullable we use a [union type](https://dotty.epfl.ch/docs/reference/new-types/union-types.html)
15
15
16
+
```scala
17
+
valx:String|Null=null// ok
16
18
```
17
-
val x: String|Null = null // ok
19
+
20
+
A nullable type could have null value during runtime; hence, it is not safe to select a member without checking its nullity.
21
+
```scala
22
+
x.trim // error: trim is not member of String | Null
18
23
```
19
24
20
25
Explicit nulls are enabled via a `-Yexplicit-nulls` flag.
@@ -24,13 +29,30 @@ Read on for details.
24
29
## New Type Hierarchy
25
30
26
31
When explicit nulls are enabled, the type hierarchy changes so that `Null` is only a subtype of
27
-
`Any`, as opposed to every reference type.
32
+
`Any`, as opposed to every reference type, which means `null` is no longer a value of `AnyRef` and its subtypes.
28
33
29
34
This is the new type hierarchy:
30
35

31
36
32
37
After erasure, `Null` remains a subtype of all reference types (as forced by the JVM).
33
38
39
+
## Working with Null
40
+
41
+
To make working with nullable values easier, we propose a utility function to the standard library:
42
+
An extension method `.nn` to "cast away" nullability.
43
+
44
+
```scala
45
+
extension [T](x: T|Null) defnn: x.type&T=
46
+
if x ==nullthen
47
+
thrownewNullPointerException("tried to cast away nullability, but value is null")
48
+
else x.asInstanceOf[x.type&T]
49
+
```
50
+
51
+
This means that given `x: String|Null`, `x.nn` has type `String`, so we can call all the
52
+
usual methods on it. Of course, `x.nn` will throw a NPE if `x` is `null`.
53
+
54
+
Don't use `.nn` on mutable variables directly, because it may introduce an unknown type into the type of the variable.
55
+
34
56
## Unsoundness
35
57
36
58
The new type system is unsound with respect to `null`. This means there are still instances where an expression has a non-nullable type like `String`, but its value is actually `null`.
@@ -71,24 +93,6 @@ y == x // ok
71
93
(x: Any) ==null// ok
72
94
```
73
95
74
-
## Working with Null
75
-
76
-
To make working with nullable values easier, we propose adding a few utilities to the standard library.
77
-
So far, we have found the following useful:
78
-
79
-
- An extension method `.nn` to "cast away" nullability
80
-
81
-
```scala
82
-
def[T] (x: T|Null) nn: x.type&T=
83
-
if (x ==null) thrownewNullPointerException("tried to cast away nullability, but value is null")
84
-
else x.asInstanceOf[x.type&T]
85
-
```
86
-
87
-
This means that given`x: String|Null`, `x.nn` has type`String`, so we can call all the
88
-
usual methods on it. Of course, `x.nn` will throw a NPEif `x` is `null`.
89
-
90
-
Don't use `.nn` on mutable variables directly, because it may introduce an unknown typeinto the typeof the variable.
91
-
92
96
## Java Interop
93
97
94
98
The compiler can load Java classes in two ways: from source or from bytecode. In either case,
@@ -249,7 +253,7 @@ We illustrate the rules with following examples:
249
253
250
254
### Override check
251
255
252
-
When we check overriding between Scala classes and Java classes, the rules are relaxed for `Null` typewiththis feature.
256
+
When we check overriding between Scala classes and Java classes, the rules are relaxed for `Null` type with this feature, in order to help users to working with Java libraries.
253
257
254
258
Suppose we have Java method `Stringf(Stringx)`, we can override this method in Scala in any of the following forms:
It is difficult to work with nullable values, we introduce a language feature `unsafeNulls`. Inside this "unsafe" scope, all `T | Null` values can be used as `T`, and `Null` type keeps being a subtype of `Any`.
269
-
270
-
User can import `scala.language.unsafeNulls` to create such scope, or use `-language:unsafeNulls` to enable this feature globally. The following unsafe null operations will apply to all nullable types:
271
-
1. select member of `T` on `T | Null` object
272
-
2. call extension methods of `T` on `T | Null`
273
-
3. convert `T1` to `T2` if `T1.stripAllNulls <:< T2.stripAllNulls` or `T1` is `Null` and `T2` has null value after erasure
274
-
275
-
The intention of this `unsafeNulls` is to give users a better migration path for explicit nulls. Projects for Scala 2 or regular dotty can try this by adding `-Yexplicit-nulls -language:unsafeNulls` to the compile options. A small number of manual modifications are expected (for example, some code relies on the fact of `Null <:< AnyRef`). To migrate to full explicit nulls in the future, `-language:unsafeNulls` can be dropped and add `import scala.language.unsafeNulls` only when needed.
276
-
277
-
```scala
278
-
deff(x: String):String=???
279
-
280
-
importscala.language.unsafeNulls
281
-
282
-
vals:String|Null=???
283
-
vala:String= s // unsafely convert String | Null to String
284
-
285
-
valb1= s.trim() // call .trim() on String | Null unsafely
286
-
valb2= b1.length()
287
-
288
-
f(s).trim() // pass String | Null as an argument of type String unsafely
Without the `unsafeNulls`, all these unsafe operations will not be compiled.
298
-
299
-
`unsafeNulls` also works for extension methods and implicit search.
300
-
301
-
```scala
302
-
importscala.language.unsafeNulls
303
-
304
-
valx="hello, world!".split("").map(_.length)
305
-
306
-
givenConversion[String, Array[String]] = _ =>???
307
-
308
-
valy:String|Null=???
309
-
valz:Array[String|Null] = y
310
-
```
270
+
Note that some of the definitions could cause unsoundness. For example, the return type is not nullable, but a `null` value is actually returned.
311
271
312
272
## Flow Typing
313
273
@@ -473,6 +433,53 @@ We don't support:
473
433
}
474
434
```
475
435
436
+
### UnsafeNulls
437
+
438
+
It is difficult to work with nullable values, we introduce a language feature `unsafeNulls`. Inside this "unsafe" scope, all `T | Null` values can be used as `T`, and `Null` type keeps being a subtype of `Any`.
439
+
440
+
Users can import `scala.language.unsafeNulls` to create such scopes, or use `-language:unsafeNulls` to enable this feature globally. The following unsafe null operations will apply to all nullable types:
441
+
1. select member of `T` on `T | Null` object
442
+
2. call extension methods of `T` on `T | Null`
443
+
3. convert `T1` to `T2` if `T1.stripAllNulls <:< T2.stripAllNulls` or `T1` is `Null` and `T2` has null value after erasure
444
+
4. allow equality check between `T` and `T | Null`
445
+
446
+
The intention of this `unsafeNulls` is to give users a better migration path for explicit nulls. Projects for Scala 2 or regular dotty can try this by adding `-Yexplicit-nulls -language:unsafeNulls` to the compile options. A small number of manual modifications are expected (for example, some code relies on the fact of `Null <:< AnyRef`). To migrate to full explicit nulls in the future, `-language:unsafeNulls` can be dropped and add `import scala.language.unsafeNulls` only when needed.
447
+
448
+
```scala
449
+
def f(x: String): String = ???
450
+
451
+
import scala.language.unsafeNulls
452
+
453
+
val s: String | Null = ???
454
+
val a: String = s // unsafely convert String | Null to String
455
+
456
+
val b1 = s.trim() // call .trim() on String | Null unsafely
457
+
val b2 = b1.length()
458
+
459
+
f(s).trim() // pass String | Null as an argument of type String unsafely
460
+
461
+
val c: String = null // Null to String
462
+
463
+
val d1: Array[String] = ???
464
+
val d2: Array[String | Null] = d1 // unsafely convert Array[String] to Array[String | Null]
465
+
val d3: Array[String] = Array(null) // unsafe
466
+
```
467
+
468
+
Without the `unsafeNulls`, all these unsafe operations will not be typechecked.
469
+
470
+
`unsafeNulls` also works for extension methods and implicit search.
471
+
472
+
```scala
473
+
import scala.language.unsafeNulls
474
+
475
+
val x = "hello, world!".split(" ").map(_.length)
476
+
477
+
given Conversion[String, Array[String]] = _ => ???
478
+
479
+
val y: String | Null = ???
480
+
val z: Array[String | Null] = y
481
+
```
482
+
476
483
## Binary Compatibility
477
484
478
485
Our strategy for binary compatibility with Scala binaries that predate explicit nulls
0 commit comments