-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
134 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
# Values, variables, and value categories | ||
|
||
<!-- | ||
Part of the Carbon Language project, under the Apache License v2.0 with LLVM | ||
Exceptions. See /LICENSE for license information. | ||
SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
--> | ||
|
||
<!-- toc --> | ||
|
||
<!-- tocstop --> | ||
|
||
## Value categories | ||
|
||
Every value in Carbon has a | ||
[value category](<https://en.wikipedia.org/wiki/Value_(computer_science)#lrvalue>) | ||
that is either an L-value or an R-value. | ||
|
||
_L-values_ are _located values_. They represent _storage_ and have a stable | ||
address. They are in principle mutable, although their type's API may limit the | ||
mutating operations available. | ||
|
||
_R-values_ are _readonly values_. They cannot be mutated in any way and may not | ||
have storage or a stable address. | ||
|
||
## Binding patterns and local variables with `let` and `var` | ||
|
||
[_Binding patterns_](/docs/design/README.md#binding-patterns) produce named | ||
R-values by default. This is the desired default for many pattern contexts, | ||
especially function parameters. R-values are a good model for "input" function | ||
parameters which are the dominant and default style of function parameters: | ||
|
||
```carbon | ||
fn Sum(x: i32, y: i32) -> i32 { | ||
// `x` and `y` are R-values here. We can read them, but not modify. | ||
return x + y; | ||
} | ||
``` | ||
|
||
A pattern can be introduced with the `var` keyword to create a _variable | ||
pattern_. This creates an L-value including the necessary storage. Every binding | ||
pattern name introduced within a variable pattern is also an L-value. When initialized, these patterns *move* their | ||
|
||
Local patterns can be introduced with `let` to get the default behavior of a | ||
readonly pattern, or they can be directly introduced with `var` to form a | ||
variable pattern and declare mutable local variables. | ||
|
||
### Pattern match control flow | ||
|
||
The most powerful form and easiest to explain form of pattern matching is a | ||
dedicated control flow construct that subsumes the `switch` of C and C++ into | ||
something much more powerful, `match`. This is not a novel construct, and is | ||
widely used in existing languages (Swift and Rust among others) and is currently | ||
under active investigation for C++. Carbon's `match` can be used as follows: | ||
|
||
``` | ||
fn Bar() -> (i32, (f32, f32)); | ||
fn Foo() -> f32 { | ||
match (Bar()) { | ||
case (42, (x: f32, y: f32)) => { | ||
return x - y; | ||
} | ||
case (p: i32, (x: f32, _: f32)) if (p < 13) => { | ||
return p * x; | ||
} | ||
case (p: i32, _: auto) if (p > 3) => { | ||
return p * Pi; | ||
} | ||
default => { | ||
return Pi; | ||
} | ||
} | ||
} | ||
``` | ||
|
||
There is a lot going on here. First, let's break down the core structure of a | ||
`match` statement. It accepts a value that will be inspected, here the result of | ||
the call to `Bar()`. It then will find the _first_ `case` that matches this | ||
value, and execute that block. If none match, then it executes the default | ||
block. | ||
|
||
Each `case` contains a pattern. The first part is a value pattern | ||
(`(p: i32, _: auto)` for example) optionally followed by an `if` and boolean | ||
predicate. The value pattern has to match, and then the predicate has to | ||
evaluate to `true` for the overall pattern to match. Value patterns can be | ||
composed of the following: | ||
|
||
- An expression (`42` for example), whose value must be equal to match. | ||
- An identifier to bind the value to, followed by a colon (`:`) and a type | ||
(`i32` for example). An underscore (`_`) may be used instead of the | ||
identifier to discard the value once matched. | ||
- A tuple destructuring pattern containing a tuple of value patterns | ||
(`(x: f32, y: f32)`) which match against tuples and tuple-like values by | ||
recursively matching on their elements. | ||
- An unwrapping pattern containing a nested value pattern which matches | ||
against a variant or variant-like value by unwrapping it. | ||
|
||
In order to match a value, whatever is specified in the pattern must match. | ||
Using `auto` for a type will always match, making `_: auto` the wildcard | ||
pattern. | ||
|
||
### Pattern matching in local variables | ||
|
||
Value patterns may be used when declaring local variables to conveniently | ||
destructure them and do other type manipulations. However, the patterns must | ||
match at compile time, so they can't use an `if` clause. | ||
|
||
``` | ||
fn Bar() -> (i32, (f32, f32)); | ||
fn Foo() -> i32 { | ||
var (p: i32, _: auto) = Bar(); | ||
return p; | ||
} | ||
``` | ||
|
||
This extracts the first value from the result of calling `Bar()` and binds it to | ||
a local variable named `p` which is then returned. | ||
|
||
## Open questions | ||
|
||
### Slice or array nested value pattern matching | ||
|
||
An open question is how to effectively fit a "slice" or "array" pattern into | ||
nested value pattern matching, or whether we shouldn't do so. | ||
|
||
### Generic/template pattern matching | ||
|
||
An open question is going beyond a simple "type" to things that support generics | ||
and/or templates. | ||
|
||
### Pattern matching as function overload resolution | ||
|
||
Need to flesh out specific details of how overload selection leverages the | ||
pattern matching machinery, what (if any) restrictions are imposed, etc. |