Skip to content

Commit eb95fe1

Browse files
committed
Add doc page
1 parent 8a1edce commit eb95fe1

File tree

4 files changed

+81
-2
lines changed

4 files changed

+81
-2
lines changed

compiler/src/dotty/tools/dotc/typer/Namer.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1170,7 +1170,7 @@ class Namer { typer: Typer =>
11701170
ctx.error(UnableToExtendSealedClass(pclazz), cls.sourcePos)
11711171
else if ctx.settings.strict.value then
11721172
checkFeature(nme.adhocExtensions,
1173-
i"Unless $pclazz with flags ${pclazz.flagsString} is declared 'open', its extension in a separate file",
1173+
i"Unless $pclazz is declared 'open', its extension in a separate file",
11741174
cls.topLevelClass,
11751175
parent.sourcePos)
11761176
pt

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1756,7 +1756,6 @@ class Typer extends Namer
17561756
// check value class constraints
17571757
checkDerivedValueClass(cls, body1)
17581758

1759-
17601759
// Temporarily set the typed class def as root tree so that we have at least some
17611760
// information in the IDE in case we never reach `SetRootTree`.
17621761
if (ctx.mode.is(Mode.Interactive) && ctx.settings.YretainTrees.value)
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
---
2+
layout: doc-page
3+
title: "Open Classes"
4+
---
5+
6+
An `open` modifier on a class signals that the class is planned for extensions. Example:
7+
```scala
8+
// File Writer.scala
9+
package p
10+
11+
open class Writer[T] with
12+
13+
/** Sends to stdout, can be overridden */
14+
def send(x: T) = println(x)
15+
16+
/** Send all arguments using `send` */
17+
def sendAll(xs: T*) = xs.foreach(send)
18+
19+
// File EncryptedWriter.scala
20+
package p
21+
22+
class EncryptedWriter[T: Encryptable] extends Writer[T] with
23+
override def send(x: T) = super.send(encrypt(x))
24+
```
25+
An open class typically comes with some documentation that describes
26+
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.
27+
28+
Classes that are not open can still be extended, but only if one of two alternative conditions hold:
29+
30+
- The extending class is in the same source file as the extended class. In this case, the extension is usually an internal implementation matter.
31+
32+
- The language feature `adhocExtensions` is enabled for the extending class. This is typically enabled by an import statement
33+
```scala
34+
import scala.language.adhocExtensions
35+
```
36+
in the source file of the extension, but can alternatively be provided
37+
by a command line option `-language:adhocExtensions`.
38+
If the feature is not enabled, the compiler will issue a "feature" warning. For instance, if the `open` modifier on class `Writer` is dropped, compiling `EncryptedWriter` would produce a warning:
39+
```
40+
-- Feature Warning: EncryptedWriter.scala:6:14 ----
41+
|class EncryptedWriter[T: Encryptable] extends Writer[T]
42+
| ^
43+
|Unless class Writer is declared 'open', its extension in a separate file should be enabled
44+
|by adding the import clause 'import scala.language.adhocExtensions'
45+
|or by setting the compiler option -language:adhocExtensions.
46+
```
47+
48+
### Motivation
49+
50+
When writing a class, there are three possible expectations of extensibility:
51+
52+
- The class is intended to allow extensions. This means one should expect
53+
a carefully worked out and documented extension contract for the class.
54+
55+
- Extensions of the class are forbidden, for instance to make correctness or security guarantees.
56+
57+
- There is no firm decision either way. The class is not _a priori_ intended for extensions, but if others find it useful to extend on an _ad-hoc_ basis, let them go ahead. However, they are on their own in this case. There is no documented extension contract, and future versions of the class might break the extensions (by rearranging internal call patterns, for instance).
58+
59+
The three cases are clearly distinguished by using `open` for (1), `final` for (2) and no modifier for (3).
60+
61+
It is good practice to avoid _ad-hoc_ extensions in a code base, since they tend to lead to fragile systems that are hard to evolve. But there
62+
are still some situations where these extensions are useful: for instance,
63+
to mock classes in tests, or to apply temporary patches that add features or fix bugs in library classes. That's why _ad-hoc_ extensions are permitted, but only if there is an explicit opt-in via a language feature import.
64+
65+
### Details
66+
67+
- `open` is a soft modifier. It is treated as a normal identifier
68+
unless it is in modifier position.
69+
- An `open` class cannot be `final` or `sealed`.
70+
- Traits or `abstract` classes are always `open`, so `open` is redundant for them.
71+
72+
### Relatinship with `sealed`
73+
74+
A class that is neither `abstract` nor `open` is similar to a `sealed` class`: it can still be extended, but only in the same compilation unit. The difference is what happens if an extension of the class is attempted in a different compilation unit. For a `sealed` class, this is an error, whereas for a simple non-open class, this is still permitted provided the `adhocExtensions` feature is enabled, and gives a warning otherwise.
75+
76+
### Migration
77+
78+
`open` is a new modifier in Scala 3. To allow cross compilation between Scala 2.13 and Scala 3.0 without warnings, the feature warning for ad-hoc extensions is produced only under `-strict`. It will be produced by default from Scala 3.1 on.

docs/sidebar.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ sidebar:
9393
url: docs/reference/other-new-features/export.html
9494
- title: Opaque Type Aliases
9595
url: docs/reference/other-new-features/opaques.html
96+
- title: Open Classes
97+
url: docs/reference/other-new-features/open-classes.html
9698
- title: Parameter Untupling
9799
url: docs/reference/other-new-features/parameter-untupling.html
98100
- title: Kind Polymorphism

0 commit comments

Comments
 (0)