-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Update CC Docs Until scoped-caps.md #24677
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
8f6cf6a to
d73dd15
Compare
b3da99d to
e8699e3
Compare
48d825e to
a7bcfbb
Compare
docs/_docs/reference/experimental/capture-checking/polymorphism.md
Outdated
Show resolved
Hide resolved
| #### Tracking the evolution of mutable objects | ||
| A common use case for explicit capture parameters is when a mutable object’s reachable capabilities | ||
| _grow_ due to mutation. For example, concatenating effectful iterators: | ||
| ```scala | ||
| class ConcatIterator[A, C^](var iterators: mutable.List[IterableOnce[A]^{C}]): | ||
| def concat(it: IterableOnce[A]^): ConcatIterator[A, {C, it}]^{this, it} = | ||
| iterators ++= it // ^ | ||
| this // track contents of `it` in the result | ||
| ``` | ||
| In such a scenario, we also should ensure that any pre-existing alias of a `ConcatIterator` object should become | ||
| inaccessible after invoking its `concat` method. This is achieved with [mutation and separation tracking](separation-checking.md) which are currently in development. | ||
| In such cases, the type system must ensure that any existing aliases of the iterator become invalid | ||
| after mutation. This is handled by [mutation tracking](mutability.md) and [separation tracking](separation-checking.md), which are currently under development. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is a poor example, as it instantly leads the reader into writing this (very buggy, doesn't guarantee anything until separation checking) code.
The problem with mutable collections with non-type-parameter element types is that you cannot use implicit capture polymorphism: they get instantiated with the elements at the construction of the collection.
class IteratorList(private var iterators: mutable.List[Iterator[A]^]):
// ^ this cap will be instantiated with whatever is in the initial list
// ...
def +=(it: Iterator[A]^) = iterators += it
val xs = IteratorList(mutable.List.empty) // forcefully instantiated to {}(also we don't have a name to refer to the capture set of the elements, when we write the += method)
Therefore, we need a capture set variable, to allow the user/inference to specify a capture set suitable for the whole usage of the collection.
class IteratorList[C^](private var iterators: mutable.List[Iterator[A]^{C}]):
// ...
def +=(it: Iterator[A]^{C}) = iterators += it
val xs = IteratorList(mutable.List.empty)
xs += Iterator(async)
xs += Iterator(io)
// inference will (probably) find out that xs: IteratorList[{async, io}] Note that this capture set will not change, it's part of the type: it means you have to be able to name all the captures of all the elements at the point of creating the collection. If you want a growing capture set, it's not sound until separation checking.
docs/_docs/reference/experimental/capture-checking/scoped-caps.md
Outdated
Show resolved
Hide resolved
| ... | ||
| ``` | ||
|
|
||
| Each capability has a _level_ corresponding to the local `cap` of its defining scope. The level determines where a capability can flow: it can flow into `cap`s at the same level or more deeply nested, but not outward to enclosing scopes (which would mean a capability lives longer than its lexical lifetime). The compiler computes a capability's level by walking up the ownership chain until reaching a symbol that represents a level boundary. Level boundaries are: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: I think flow is a vague word. Maybe "The level determines where a capability can be part of: it can be a part of caps at the same level or more deeply nested..." or something like "assigned to"?
Update: I think we used in a lot of places, so maybe it's fine?
docs/_docs/reference/experimental/capture-checking/scoped-caps.md
Outdated
Show resolved
Hide resolved
| ``` | ||
|
|
||
| The closure is declared pure (`() -> Unit`), meaning its local `cap` is the empty set. The capability `fs` cannot flow into an empty set, so the checker rejects this. | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps we should say something about the withFile[T] pattern here?
|
|
||
| **Examples:** | ||
|
|
||
| - `A => B` is an alias type that expands to `A ->{cap} B`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What should A => B => C be extended to? I think it's quite unclear here
docs/_docs/reference/experimental/capture-checking/scoped-caps.md
Outdated
Show resolved
Hide resolved
Co-authored-by: Natsu Kagami <natsukagami@gmail.com>
Continuation of #24626
[skip ci]