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
In fact, only a single character was added: `c`'s type now carries a universal capability. This signature enforces at the same time two desirable properties:
37
37
38
-
- Matrices `a`, and `b` are read-only; `multiply` will not call their update method. By contrast, matrix`c` can be updated.
38
+
- Matrices `a`, and `b` are read-only; `multiply` will not call their update method. By contrast, the`c` matrix can be updated.
39
39
- Matrices `a` and `b` are different from matrix `c`, but `a` and `b` could refer to the same matrix.
40
40
41
41
So, effectively, anything that can be updated must be unaliased.
@@ -62,11 +62,11 @@ a new soft modifier `update`.
62
62
classRef(init: Int) extendsMutable:
63
63
privatevarcurrent= init
64
64
defget:Int= current
65
-
update defput(x: Int):Unit= current = x
65
+
update defset(x: Int):Unit= current = x
66
66
```
67
67
`update` can only be used in classes or objects extending `Mutable`. An update method is allowed to access exclusive capabilities in the method's environment. By contrast, a normal method in a type extending `Mutable` may access exclusive capabilities only if they are defined locally or passed to it in parameters.
68
68
69
-
In class `Ref`, the `put` should be declared as an update method since it accesses the exclusive write capability of the variable `current` in its environment.
69
+
In class `Ref`, the `set` should be declared as an update method since it accesses the exclusive write capability of the variable `current` in its environment.
70
70
71
71
`update` can also be used on an inner class of a class or object extending `Mutable`. It gives all code in the class the right
72
72
to access exclusive capabilities in the class environment. Normal classes
@@ -84,7 +84,7 @@ Normal method members of `Mutable` classes cannot call update methods. This is i
84
84
85
85
An update method cannot implement or override a normal method, whereas normal methods may implement or override update methods. Since methods such as `toString` or `==` inherited from Object are normal methods, it follows that none of these methods may be implemented as an update method.
86
86
87
-
The `apply` method of a function type is also a normal method, hence `Mutable` classes may not implement a function type with an update method as the `apply` method.
87
+
The `apply` method of a function type is also a normal method, hence `Mutable` classes may not implement a function type using an update method as the `apply` method.
88
88
89
89
## Mutable Types
90
90
@@ -154,7 +154,7 @@ If `x` is an exclusive capability of a type extending `Mutable`, `x.rd` is its a
154
154
A read-only capability can be seen as a classified capability
155
155
using a classifier trait `Read` that extends `Mutable`. I.e.
156
156
`x.rd` can be seen as being essentially the same as `x.only[Read]`.
157
-
Currently, this precise equivalence is still waiting to be implemented.
157
+
(Currently, this precise equivalence is still waiting to be implemented.)
158
158
159
159
**Implicitly added capture sets**
160
160
@@ -165,10 +165,6 @@ For instance, consider the matrix multiplication method mentioned previously:
@@ -177,7 +173,7 @@ Separation checking will then make sure that `a` and `b` must be different from
177
173
178
174
## Accesses to Mutable Types
179
175
180
-
An access `p.m` to an update method or class `m` in a mutable type is permitted only if the type of the prefix `M` retains exclusive capabilities. If `M` is pure or its capture set has only shared
176
+
An access `p.m` to an update method or class `m` in a mutable type is permitted only if the type `M`of the prefix `p` retains exclusive capabilities. If `M` is pure or its capture set has only shared
181
177
capabilities then the access is not permitted.
182
178
183
179
A _read-only access_ is a reference `x` to a type extending `Mutable` that is either
@@ -197,8 +193,8 @@ val f = () => x.get // f: () ->{x.rd} Unit
197
193
valg= () => x.set(1) // g: () ->{x} Unit
198
194
```
199
195
200
-
`f` accesses a regular method, so it charges only `x.rd` to its environment which shows up in its capture set. Bu contrast, `g`
201
-
accesses an update method of `x`,so its capture set is `{x}`.
196
+
`f` accesses a regular method, so it charges only `x.rd` to its environment which shows up in its capture set. By contrast, `g`
197
+
accesses an update method of `x`,so its capture set is `{x}`.
202
198
203
199
A reference to a mutable type with an exclusive capture set can be widened to a reference with a read-only set. For instance, the following is OK:
204
200
```scala
@@ -237,7 +233,7 @@ The subcapturing theory for sets is then as before, with the following additiona
237
233
238
234
## Separation Checking
239
235
240
-
The idea behind separation checking is simple: We now interpret each occurrence of `cap` as a separate top capability. This includes derived syntaxes like `A^` and `B => C`. We further keep track during capture checking which capabilities are subsumed by each `cap`. If capture checking widens a capability `x` to the universal capability, we say `x` is _hidden_ by the universal. The rule then is that any capability hidden by a universal capability `capᵢ` cannot be referenced independently or hidden in another cap in code that can see `capᵢ`.
236
+
The idea behind separation checking is simple: We now interpret each occurrence of `cap` as a separate top capability. This includes derived syntaxes like `A^` and `B => C`. We further keep track during capture checking which capabilities are subsumed by each `cap`. If capture checking widens a capability `x` to a top capability`capᵢ`, we say `x` is _hidden_ by `capᵢ`. The rule then is that any capability hidden by a top capability `capᵢ` cannot be referenced independently or hidden in another `capⱼ` in code that can see `capᵢ`.
241
237
242
238
Here's an example:
243
239
```scala
@@ -247,7 +243,7 @@ val x: C^ = y
247
243
```
248
244
This principle ensures that capabilities such as `x` that have `cap` as underlying capture set are un-aliased or "fresh". Any previously existing aliases such as `y` in the code above are inaccessible as long as `x` is also visible.
249
245
250
-
The idea behind separation checking is simple: We now interpret each occurrence of `cap` as a separate top capability. This includes derived syntaxes like `A^`and `B => C`. We further keep track during capture checking which capabilities are subsumed by each `cap`. If capture checking widens a capability `x` to the universal capability, we say `x`is _hidden_ by the universal. The rule then is that any capability hidden by a universal capability `capᵢ` cannot be referenced independently or hidden in another cap in code that can see `capᵢ`.
246
+
Separation checking applies only to exclusive capabilities and their read-only versions. Any capability extending `SharedCapability` in its type is exempted; the following definitions and rules do not apply to them.
251
247
252
248
**Definitions:**
253
249
@@ -256,7 +252,7 @@ The idea behind separation checking is simple: We now interpret each occurrence
256
252
- The _transitive capture set_`tcs(C)` of a capture set C is the union
257
253
of `tcs(c)` for all elements `c` of `C`.
258
254
259
-
- Two capture sets _interfere_ if one contains an exclusive capability `x` and the other also contains `x` or contains the read-only capability `x.rd`. Conversely, two capture sets are _separated_ if they don't interfere.]
255
+
- Two capture sets _interfere_ if one contains an exclusive capability `x` and the other also contains `x` or contains the read-only capability `x.rd`. Conversely, two capture sets are _separated_ if their transitive capture sets don't interfere.
260
256
261
257
Separation checks are applied in the following scenarios:
262
258
@@ -281,11 +277,11 @@ val r = Ref(1)
281
277
valplusOne= r.set(r.get +1)
282
278
seq(plusOne, plusOne)
283
279
```
284
-
Without the explicit mention of parameter `f` in the capture set of parameter `g` of `seq` we'd get a separation error, since the capture sets of both arguments contain `r` and are therefore not separated.
280
+
Without the explicit mention of parameter `f` in the capture set of parameter `g` of `seq` we'd get a separation error, since the transitive capture sets of both arguments contain `r` and are therefore not separated.
285
281
286
282
### Checking Statement Sequences
287
283
288
-
When a capability `x` is used at some point in a statement sequence, check that `x` does not appear in the hidden set of a previous definition.
284
+
When a capability `x` is used at some point in a statement sequence, we check that `{x}` is separated from the hidden sets of all previous definitions.
289
285
290
286
Example:
291
287
```scala
@@ -318,7 +314,7 @@ val b: (Ref^, Ref^) = (a, a) // error
318
314
valc: (Ref^, Ref^{a}) = (a, a) // error
319
315
vald: (Ref^{a}, Ref^{a}) = (a, a) // ok
320
316
```
321
-
Here, the definition of `b` is in error since the hidden sets of the two `^` in its type both contain `a`. Likewise, the definition of `c` is in error since the hidden set of the `^` in its type contains `a`, which is also part of a capture set somewhere else in the type. On the other hand, the definition of `d` is legal since there are no hidden sets to check.
317
+
Here, the definition of `b` is in error since the hidden sets of the two `^`s in its type both contain `a`. Likewise, the definition of `c` is in error since the hidden set of the `^` in its type contains `a`, which is also part of a capture set somewhere else in the type. On the other hand, the definition of `d` is legal since there are no hidden sets to check.
322
318
323
319
### Checking Return Types
324
320
@@ -332,13 +328,12 @@ def newRef(): Ref^ =
332
328
vala=Ref(1)
333
329
a
334
330
```
335
-
But the next line would cause a separation error:
331
+
But the next definitions would cause a separation error:
336
332
```scala
337
333
vala=Ref(1)
338
334
defnewRef():Ref^= a // error
339
335
```
340
-
Here the rule is that the hidden set of a fresh cap in a return type cannot reference capabilities defined outside of the function. What about parameters?
341
-
Here's another illegal version:
336
+
The rule is that the hidden set of a fresh cap in a return type cannot reference exclusive or read-only capabilities defined outside of the function. What about parameters? Here's another illegal version:
342
337
```scala
343
338
defincr(a: Ref^):Ref^=
344
339
a.set(a.get +1)
@@ -377,16 +372,16 @@ of a previous application.
377
372
378
373
Consume parameters enforce linear access to resources. This can be very useful. As an example, consider Scala's buffers such as `ListBuffer` or `ArrayBuffer`. We can treat these buffers as if they were purely functional, if we can enforce linear access.
379
374
380
-
We can define a function `linearAdd` that adds elements to buffers without violating referential transparency:
375
+
For instance, we can define a function `linearAdd` that adds elements to buffers in-place without violating referential transparency:
`linearAdd` returns a fresh buffer resulting from appending `elem` to `buf`. It overwrites `buf`, but that's OK since the `@consume` annotation on `buf`makes the argument is not used after the call.
380
+
`linearAdd` returns a fresh buffer resulting from appending `elem` to `buf`. It overwrites `buf`, but that's OK since the `@consume` annotation on `buf` ensures that the argument is not used after the call.
386
381
387
382
### Consume Methods
388
383
389
-
Buffers in Scala's standard library use a single-argument method `+=` instead of a two argument global function. We can enforce linearity in this case by adding the `@consume` annotation to the method itself.
384
+
Buffers in Scala's standard library use a single-argument method `+=` instead of a two argument global function like `linearAdd`. We can enforce linearity in this case by adding the `@consume` annotation to the method itself.
390
385
```scala
391
386
classBuffer[T] extendsMutable:
392
387
@consume update def+=(x: T):Buffer[T]^=this// ok
@@ -397,5 +392,5 @@ val b = Buffer[Int]() += 1 += 2
397
392
valc= b +=3
398
393
// b cannot be used from here
399
394
```
400
-
and this code is equivalent (but a lot more efficient) to functional append with `+`.
395
+
This code is equivalent to functional append with `+`, and is at the same time more efficient since it re-uses the storage of the argument buffer.
0 commit comments