Skip to content

Commit 47c1d89

Browse files
committed
Docs and tests
1 parent 738b24a commit 47c1d89

File tree

2 files changed

+173
-0
lines changed

2 files changed

+173
-0
lines changed
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
---
2+
layout: doc-page
3+
title: "Export"
4+
---
5+
6+
An export clause defines aliases for selected members of an object. Example:
7+
```scala
8+
class BitMap
9+
class InkJet
10+
11+
class Printer {
12+
type PrinterType
13+
def print(bits: BitMap): Unit = ???
14+
def status: List[String] = ???
15+
}
16+
17+
class Scanner {
18+
def scan(): BitMap = ???
19+
def status: List[String] = ???
20+
}
21+
22+
class Copier {
23+
private val printUnit = new Printer { type PrinterType = InkJet }
24+
private val scanUnit = new Scanner
25+
26+
export scanUnit.scan
27+
export printUnit.{status => _, _}
28+
29+
def status: List[String] = printUnit.status ++ scanUnit.status
30+
}
31+
```
32+
The two `export` clauses define the following _export aliases_ in class `Copier`:
33+
```scala
34+
final def scan(): BitMap = scanUnit.scan()
35+
final def print(bits: BitMap): Unit = printUnit.print(bits)
36+
final type PrinterType = printUnit.PrinterType
37+
```
38+
They can be accessed inside `Copier` as well as from outside:
39+
```scala
40+
val copier = new Copier
41+
copier.print(copier.scan())
42+
```
43+
An export clause has the same format as an import clause. Its general form is:
44+
```scala
45+
export path . { sel_1, ..., sel_n }
46+
export implied path . { sel_1, ..., sel_n }
47+
```
48+
It consists of a qualifier expression `path`, which must be a stable identifier, followed by
49+
one or more selectors `sel_i` that identify what gets an alias. Selectors can be
50+
of one of three forms:
51+
52+
- A _simple selector_ `x` creates aliases for all eligible members of `path` that are named `x`.
53+
- A _renaming selector_ `x => y` creates aliases for all eligible members of `path` that are named `x`, but the alias is named `y` instead of `x`.
54+
- An _omitting selector_ `x => _` presents `x` from being aliased by a subsequent
55+
wildcard selector.
56+
- A _wildcard selector_ creates aliases for all eligible members of `path` except for
57+
those members that are named by a previous simple, renaming, or omitting selector.
58+
59+
A member is _eligible_ if all of the following holds:
60+
61+
- its owner is not a base class of the class(*) containing the export clause,
62+
- it is accessible at the export clause,
63+
- it is not a constructor, nor the (synthetic) class part of an object,
64+
- it is an `implied` instance (or an old-style `implicit` value)
65+
if and only if the export is `implied`.
66+
67+
Type members are aliased by type definitions, and term members are aliased by method definitions. Export aliases copy the type and value parameters of the members they refer to.
68+
Export aliases are always `final`. Aliases of implied instances are again `implied` (and aliases of old-style implicits are `implicit`). There are no other modifiers that can be given to an alias. This has the following consequences for overriding:
69+
70+
- Export aliases cannot be overridden, since they are final.
71+
- Export aliases cannot override concrete members in base classes, since they are
72+
not marked `override`.
73+
- However, export aliases can implement deferred members of base classes.
74+
75+
Export aliases for value definitions are marked by the compiler as "stable". This means
76+
that they they can be used as parts of stable identifier paths, even though they are technically methods. For instance, the following is OK:
77+
```scala
78+
class C { type T }
79+
object O { val c: C = ... }
80+
export O.c
81+
def f: c.T = ...
82+
```
83+
84+
Export clauses can appear in classes or they can appear at the top-level. An export clause cannot appear as a statement in a block.
85+
86+
(*) Note: Unless otherwise stated, the term "class" in this discussion also includes object and trait definitions.
87+
88+
### Motivation
89+
90+
It is a standard recommendation to prefer aggregation over inheritance. This is really an application of the principle of least power: Aggregation treats components as blackboxes whereas inheritance can affect the internal workings of components through overriding. Sometimes the close coupling implied by inheritance is the best solution for a problem, but where this is not necessary the looser coupling of aggregation is better.
91+
92+
So far, object oriented languages including Scala made it much easer to use inheritance than aggregation. Inheritance only requires an `extends` clause whereas aggregation required a verbose elaboration of a sequence of forwarders. So in that sense, OO languages are pushing
93+
programmers to a solution that is often too powerful. Export clauses redress the balance. They make aggregation relationships as concise and easy to express as inheritance relationships. Export clauses also offer more flexibility than extends clauses since members can be renamed or omitted.
94+
95+
Export clauses also fill a gap opened by the shift from package objects to toplevel definitions. One occasionally useful idiom that gets lost in this shift is a package object inheriting from some class. The idiom is often used in a facade like pattern, to make members
96+
of internal compositions available to users of a package. Toplevel definitions are not wrapped in a user-defined object, so they can't inherit anything. However, toplevel definitions can be export clauses, which supports the facade design pattern in a safer and
97+
more flexible way.
98+
99+
### Syntax changes:
100+
101+
```
102+
TemplateStat ::= ...
103+
| Export
104+
TopStat ::= ...
105+
| Export
106+
Export ::= ‘export’ [‘implied’] ImportExpr {‘,’ ImportExpr}
107+
```
108+
109+
### Elaboration of Export Clauses
110+
111+
Export clauses raise questions about the order of elaboration during type checking.
112+
Consider the following example:
113+
```scala
114+
class B { val c: Int }
115+
object a { val b = new B }
116+
export a._
117+
export b._
118+
}
119+
```
120+
Is the `export b._` clause legal? If yes, what does it export? Is it equivalent to `export a.b._`? What about if we swap the last two clauses?
121+
```
122+
export b._
123+
export a._
124+
```
125+
To avoid tricky questions like these, we fix the elaboration order of exports as follows.
126+
127+
Export clauses are processed when the type information of the enclosing object or class is completed. Completion so far consisted of the following steps:
128+
129+
1. Elaborate any annotations of the class.
130+
2. Elaborate the parameters of the class.
131+
3. Elaborate the self type of the class, if one is given.
132+
4. Enter all definitions of the class as class members, with types to be completed
133+
on demand.
134+
5. Determine the types of all parents of the class.
135+
136+
With export clauses, the following steps are added:
137+
138+
6. Compute the types of all paths in export clauses in a context logically
139+
inside the class but not considering any imports or exports in that class.
140+
7. Enter export aliases for the eligible members of all paths in export clauses.
141+
142+
It is important that steps 6 and 7 are done in sequence: We first compute the types of _all_
143+
paths in export clauses and only after this is done we enter any export aliases as class members. This means that a path of an export clause cannot refer to an alias made available
144+
by another export clause of the same class object.

tests/pos/reference/exports.scala

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
class BitMap
2+
class InkJet
3+
4+
class Printer {
5+
type PrinterType
6+
def print(bits: BitMap): Unit = ???
7+
def status: List[String] = ???
8+
}
9+
10+
class Scanner {
11+
def scan(): BitMap = ???
12+
def status: List[String] = ???
13+
}
14+
15+
class Copier {
16+
private val printUnit = new Printer { type PrinterType = InkJet }
17+
private val scanUnit = new Scanner
18+
19+
export scanUnit.scan
20+
export printUnit.{status => _, _}
21+
22+
def status: List[String] = printUnit.status ++ scanUnit.status
23+
}
24+
25+
class C { type T }
26+
object O { val c: C = ??? }
27+
export O.c
28+
def f: c.T = ???
29+

0 commit comments

Comments
 (0)