Skip to content

Commit 90f1c83

Browse files
committed
Add generality notes: Function defaults, and function <-> block/stmt unification
1 parent 74fb716 commit 90f1c83

File tree

4 files changed

+275
-111
lines changed

4 files changed

+275
-111
lines changed

docs/cpp2/aliases.md

Lines changed: 0 additions & 97 deletions
This file was deleted.

docs/cpp2/declarations.md

Lines changed: 165 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
# Declaration syntax
1+
# Declarations and aliases
22

3-
## Overview: Unified declaration syntax
3+
## Unified declarations
44

55
All Cpp2 declarations are written as **"_name_ `:` _kind_ `=` _statement_"**.
66

@@ -20,10 +20,70 @@ Various parts of the syntax allow a `_` "don't care" wildcard or can be omitted
2020
>
2121
> - When the type is omitted, whitespace does not matter, and writing `#!cpp x: = 0;` or `#!cpp x : = 0;` or `#!cpp x := 0;` or other whitespace is just a stylistic choice. This documentation's style uses the last one, except when there are multiple adjacent declaration lines this style lines up their `:` and `=`.
2222
>
23-
> - `==` stresses that this name will always have the given value, to express [aliases](aliases.md) and side-effect-free 'constexpr' [function aliases](aliases.md/#function-aliases).
23+
> - `==` stresses that this name will always have the given value, to express [aliases](#aliases) and side-effect-free 'constexpr' [function aliases](#function-aliases).
2424
2525

26-
## <a id="template-parameters"></a> Template parameters
26+
### <a id="unnamed-declarations"></a> Unnamed declaration expressions
27+
28+
In an expression, most declarations can be written without a name (just starting with `:`). Such unnamed declaration expressions are useful for single-use temporary variables or 'lambda' functions that don't need a name to be reused elsewhere. For example:
29+
30+
- `#!cpp :widget = 42` is an unnamed expression-local (aka temporary) object of type `widget` defined as having the initial value `#!cpp 42`. It uses the same general syntax, just
31+
32+
- `#!cpp :(x) = std::cout << x` is an unnamed expression-local generic function expression (aka lambda) defined as having the given one-statement body. The body can include [captures](expressions.md/#captures)
33+
34+
Both just omit the name and make the final `;` optional. Otherwise, they have the identical syntax and meaning as if you declared the same thing with a name outside expression scope (e.g., `w: widget = 42;` or `f: (x) = std::cout << x;`) and then used the name in the expression.
35+
36+
> Note: Throughout Cpp2, every declaration is written with `:`, and every use of `:` is a declaration.
37+
38+
39+
40+
### <a id="function-scope-unification"></a> From functions to local scopes, and back again
41+
42+
The function syntax is deliberately designed to be general, so you can omit parts. This means Cpp2 has no special "lambda function" syntax for unnamed functions; an unnamed function is really an unnamed function, written using the ordinary function just without a name. This scales all the way down to ordinary blocks and statements, which are written the same as functions that have no name or parameters.
43+
44+
We can illustrate this in two directions. First, let's start with a full function, and successively omit optional parts that we aren't currently using:
45+
46+
``` cpp title="Start with a full function, and successively omit optional parts if unused" hl_lines="1 5 9 13"
47+
// Full named function
48+
f:(x: int = init) = { /*...*/ } // x is a parameter to the function
49+
f:(x: int = init) = statement; // same, { } is implicit
50+
51+
// Omit name => anonymous function (aka 'lamba')
52+
:(x: int = init) = { /*...*/ } // x is a parameter to the function
53+
:(x: int = init) = statement; // same, { } is implicit
54+
55+
// Omit declaration => local and immediate (aka 'let' in other languages)
56+
(x: int = init) { /*...*/ } // x is a parameter to this
57+
(x: int = init) statement; // compound or single-statement
58+
59+
// Omit parameters => ordinary block or statement
60+
{ /*...*/ } // ordinary compound statement
61+
statement; // ordinary statement
62+
```
63+
64+
Conversely, we can start with an ordinary block or statement, and successively build it up to make it more powerful:
65+
66+
``` cpp title="Start with an ordinary block or statement, and successively add parts" hl_lines="1 5 9 13"
67+
// Ordinary block or statement
68+
{ /*...*/ } // ordinary compound statement
69+
statement; // ordinary statement
70+
71+
// Add parameters => more RAII locally-scoped variables
72+
(x: int = init) { /*...*/ } // x is destroyed after this
73+
(x: int = init) statement; // compound or single-statement
74+
75+
// Add declaration => treat the code as a callable object
76+
:(x: int = init) = { /*...*/ } // x is a parameter to the function
77+
:(x: int = init) = statement; // same, { } is implicit
78+
79+
// Add name => full named function
80+
f:(x: int = init) = { /*...*/ } // x is a parameter to the function
81+
f:(x: int = init) = statement; // same, { } is implicit
82+
83+
```
84+
85+
86+
### <a id="template-parameters"></a> Template parameters
2787

2888
A template parameter list is enclosed by `<` `>` angle brackets, and the parameters separated by commas. Each parameter is declared using the [same syntax as any type or object](declarations.md). If a parameter's **`:`** ***kind*** is not specified, the default is `: type`.
2989

@@ -45,7 +105,7 @@ tuple: <Ts...: type> type
45105
```
46106

47107

48-
## <a id="requires"></a> `#!cpp requires` constraints
108+
### <a id="requires"></a> `#!cpp requires` constraints
49109

50110
A `#!cpp requires` ***condition*** constraint appears at the end of the ***kind*** of a templated declaration. If the condition evaluates to `#!cpp false`, that specialization of the template is ignored as if not declared.
51111

@@ -61,7 +121,7 @@ print: <Args...: type>
61121
```
62122

63123

64-
## Examples
124+
### Examples
65125

66126
``` cpp title="Consistent declarations — name : kind = statement" linenums="1" hl_lines="2 6 10 15 24 28 32 43 49 53"
67127
// n is a namespace defined as the following scope
@@ -121,3 +181,102 @@ n: namespace
121181
```
122182

123183
> Note: `@enum` is a metafunction, which provides an easy way to opt into a group of defaults, constraints, and generated functions. For details, see [`@enum`](metafunctions.md#enum).
184+
185+
186+
## <a id="aliases"></a> Aliases
187+
188+
Aliases are pronounced **"synonym for**, and written using the same **name `:` kind `=` value** [declaration syntax](../cpp2/declarations.md) as everything in Cpp2:
189+
190+
- **name** is declared to be a synonym for **value**.
191+
192+
- **kind** can be any of the kinds: `namespace`, `type`, a function signature, or a type.
193+
194+
- **`==`**, pronounced **"defined as a synonym for"**, always precedes the value. The `==` syntax stresses that during compilation every use of the name could be equivalently replaced with the value.
195+
196+
- **value** is the expression that the **name** is a synonym for.
197+
198+
199+
### <a id="namespace-aliases"></a> Namespace aliases
200+
201+
A namespace alias is written the same way as a [namespace](namespaces.md), but using `==` and with the name of another namespace as its value. For example:
202+
203+
``` cpp title="Namespace aliases" hl_lines="1 2 4 5 8 12 16"
204+
// 'chr' is a namespace defined as a synonym for 'std::chrono'
205+
chr : namespace == std::chrono;
206+
207+
// 'chrlit' is a namespace defined as a synonym for 'std::chrono_literals'
208+
chrlit : namespace == std::chrono_literals;
209+
210+
main: () = {
211+
using namespace chrlit;
212+
213+
// The next two lines are equivalent
214+
std::cout << "1s is (std::chrono::nanoseconds(1s).count())$ns\n";
215+
std::cout << "1s is (chr::nanoseconds(1s).count())$ns\n";
216+
}
217+
// Prints:
218+
// 1s is 1000000000ns
219+
// 1s is 1000000000ns
220+
```
221+
222+
223+
### <a id="type-aliases"></a> Type aliases
224+
225+
A namespace alias is written the same way as a [type](types.md), but using `==` and with the name of another type as its value. For example:
226+
227+
``` cpp title="Type aliases" hl_lines="1 2 7 10"
228+
// 'imap<T>' is a type defined as a synonym for 'std::map<i32, T>'
229+
imap : <T> type == std::map<i32, T>;
230+
231+
main: () = {
232+
// The next two lines declare two objects with identical type
233+
map1: std::map<i32, std::string> = ();
234+
map2: imap<std::string> = ();
235+
236+
// Assertion they are the same type, using the same_as concept
237+
assert( std::same_as< decltype(map1), decltype(map2) > );
238+
}
239+
```
240+
241+
242+
### <a id="function-aliases"></a> Function aliases
243+
244+
A function alias is written the same way as a [function](functions.md), but using `==` and with a side-effect-free body as its value; the body must always return the same value for the same input arguments. For example:
245+
246+
``` cpp title="Function aliases" hl_lines="1 2 6 9 12 15"
247+
// 'square' is a function defined as a synonym for the value of 'i * i'
248+
square: (i: i32) -> _ == i * i;
249+
250+
main: () = {
251+
// It can be used at compile time, with compile time values
252+
ints: std::array<i32, square(4)> = ();
253+
254+
// Assertion that the size is the square of 4
255+
assert( ints.size() == 16 );
256+
257+
// And if can be used at run time, with run time values
258+
std::cout << "the square of 4 is (square(4))$\n";
259+
}
260+
// Prints:
261+
// the square of 4 is 16
262+
```
263+
264+
> Note: A function alias is compiled to a Cpp1 `#!cpp constexpr` function.
265+
266+
267+
### <a id="object-aliases"></a> Object aliases
268+
269+
An object alias is written the same way as an [object](objects.md), but using `==` and with a side-effect-free value. For example:
270+
271+
``` cpp title="Function aliases" hl_lines="1 2 5 6"
272+
// 'BufferSize' is an object defined as a synonym for the value 1'000'000
273+
BufferSize: i32 == 1'000'000;
274+
275+
main: () = {
276+
buf: std::array<std::byte, BufferSize> = ();
277+
assert( buf.size() == BufferSize );
278+
}
279+
```
280+
281+
> Note: An object alias is compiled to a Cpp1 `#!cpp constexpr` object.
282+

0 commit comments

Comments
 (0)