Skip to content

Commit

Permalink
add missing file
Browse files Browse the repository at this point in the history
  • Loading branch information
chandlerc committed Oct 15, 2022
1 parent f6627f2 commit 483cb12
Showing 1 changed file with 134 additions and 0 deletions.
134 changes: 134 additions & 0 deletions docs/design/values.md
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.

0 comments on commit 483cb12

Please sign in to comment.