Skip to content

Commit ecb0031

Browse files
committed
Add more functions material
Return values Branches Loops Template parameters
1 parent f0addd2 commit ecb0031

File tree

5 files changed

+176
-9
lines changed

5 files changed

+176
-9
lines changed

docs/cpp2/declarations.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ All Cpp2 declarations are written as **"_name_ `:` _kind_ `=` _statement_"**.
66

77
- The `:` is pronounced **"is a."**
88

9+
- The _kind_ can start with [template parameters](#template-parameters).
10+
911
- The `=` is pronounced **"defined as."** For the definition of something that will always have the same value, write `==`.
1012

1113
- The _statement_ is typically an expression statement (e.g., `#!cpp a + b();`) or a compound statement (e.g., `#!cpp { /*...*/ return c(d) / e; }`).
@@ -18,6 +20,23 @@ All Cpp2 declarations are written as **"_name_ `:` _kind_ `=` _statement_"**.
1820
>
1921
> - `==` stresses that this name will always have the given value, to express [aliases](./aliases.md) and side-effect-free 'constexpr' functions (e.g., `#!cpp square: (i: int) == i * i;`).
2022
23+
24+
## <a id="template-parameters"></a> Template parameters
25+
26+
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`.
27+
28+
For example:
29+
30+
``` cpp title="Declaring template parameters" hl_lines="1-3"
31+
array: <T: type, size: i32> type
32+
// parameter T is a type
33+
// parameter size is a 32-bit int
34+
= {
35+
// ...
36+
}
37+
```
38+
39+
2140
## Examples
2241

2342
``` cpp title="Consistent declarations — name : kind = statement" linenums="1" hl_lines="2 6 10 15 24 28 32 43 49 53"

docs/cpp2/functions.md

Lines changed: 150 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,30 @@
33

44
## Overview
55

6-
TODO
6+
A function is defined by writing a function signature after the `:` and a statement (expression or `{` `}` compound statement) after the `=`. After the optional [template parameters](declarations.md#template-parameters) available for all declarations, a function signatures consists of a possibly-empty [parameter list](#parameters), and an optional function [return values](#return-values).
7+
8+
For example, the minimal function named `func` that takes no parameters and returns nothing (`#!cpp void`) is:
9+
10+
``` cpp title="A minimal function"
11+
func: ( /* no parameters */ ) = { /* empty body */ }
12+
```
713
814
915
## <a id="parameters"></a> Parameters
1016
17+
The parameter list is enclosed by `(` `)` parentheses, and the parameters separated by commas. Each parameter is declared using the [same syntax as any object](declarations.md). For example:
18+
19+
``` cpp title="Declaring parameters" hl_lines="2-4"
20+
func: (
21+
x: i32, // parameter x is a 32-bit int
22+
y: std::string, // parameter y is a std::string
23+
z: std::map<i32, std::string> // parameter z is a std::map
24+
)
25+
= {
26+
// ...
27+
}
28+
```
29+
1130
There are six ways to pass parameters that cover all use cases:
1231

1332
| Parameter ***kind*** | "Pass me an `x` I can ______" | Accepts arguments that are | Special semantics | ***kind*** `x: X` Compiles to Cpp1 as |
@@ -25,7 +44,58 @@ There are six ways to pass parameters that cover all use cases:
2544

2645
## <a id="return-values"></a> Return values
2746

28-
TODO
47+
A function can return either of the following. The default is `#!cpp -> void`.
48+
49+
(1) **`#!cpp -> X`** to return a single unnamed value of type `X`, which can be `#!cpp void` to signify the function has no return value. If `X` is not `#!cpp void`, the function body must have a `#!cpp return /*value*/;` statement that returns a value of type `X` on every path that exits the function. For example:
50+
51+
``` cpp title="Functions with an unnamed return value" hl_lines="2 4 7 9 12 14"
52+
// A function returning no value (void)
53+
increment_in_place: (inout a: i32) -> void = { a++; }
54+
// Or, using syntactic defaults, the following has identical meaning:
55+
increment_in_place: (inout a: i32) = a++;
56+
57+
// A function returning a single value of type i32
58+
add_one: (a: i32) -> i32 = { return a+1; }
59+
// Or, using syntactic defaults, the following has identical meaning:
60+
add_one: (a: i32) -> i32 = a+1;
61+
62+
// A generic function returning a single value of deduced type
63+
add: <T: type, U: type> (a:T, b:U) -> decltype(a+b) = { return a+b; }
64+
// Or, using syntactic defaults, the following has identical meaning:
65+
add: (a, b) -> _ = a+b;
66+
```
67+
68+
(2) **`#!cpp -> ( /* parameter list */ )`** to return a list of named return parameters using the same [parameters](#parameters) syntax, but where the only passing styles are `out` (the default, which moves where possible) or `forward`. The function body must [initialize](objects.md#init) the value of each return-parameter `ret` in its body the same way as any other local variable. An explicit return statement is written just `#!cpp return;` and returns the named values; the function has an implicit `#!cpp return;` at the end. For example:
69+
70+
``` cpp title="Functions with multiple/named return values" hl_lines="7 9 10 22-24"
71+
set: <Key> type = {
72+
container: std::set<Key>;
73+
iterator : type == std::set<Key>::iterator;
74+
75+
// A std::set::insert-like function using named return values
76+
// instead of just a std::pair/tuple
77+
insert: (inout this, value: Key) -> (where: iterator, inserted: bool) = {
78+
set_returned := container.insert(value);
79+
where = set_returned.first;
80+
inserted = set_returned.second;
81+
}
82+
83+
ssize: (this) -> i64 = std::ssize(container);
84+
85+
// ...
86+
}
87+
88+
use_inserted_position: (_) = { }
89+
90+
main: () = {
91+
m: set<std::string> = ();
92+
ret := m.insert("xyzzy");
93+
if ret.inserted {
94+
use_inserted_position( ret.where );
95+
}
96+
assert( m.ssize() == 1 );
97+
}
98+
```
2999

30100

31101
### <a id="nodiscard-outputs"></a> Function outputs are not implicitly discardable
@@ -34,7 +104,7 @@ A function's outputs are its return values, and the "out" state of any `out` and
34104

35105
Function outputs cannot be silently discarded. To explicitly discard a function output, assign it to `_`. For example:
36106

37-
``` cpp title="No silent discard" hl_lines="10 11 22 27"
107+
``` cpp title="No silent discard" hl_lines="9 11 13 17-18 23-24 29-30"
38108
f: () -> void = { }
39109
g: () -> int = { return 10; }
40110
h: (inout x: int) -> void = { x = 20; }
@@ -44,7 +114,9 @@ main: ()
44114
f(); // ok, no return value
45115

46116
std::cout << g(); // ok, use return value
117+
47118
_ = g(); // ok, explicitly discard return value
119+
48120
g(); // ERROR, return value is ignored
49121

50122
{
@@ -78,15 +150,87 @@ main: ()
78150

79151
## <a id="branches"></a> `#!cpp if`, `#!cpp else` — Branches
80152

81-
TODO
153+
`if` and `else` are like always in C++, except that `(` `)` parentheses around the condition are not required. Instead, `{` `}` braces around a branch body *are* required. For example:
154+
155+
``` cpp title="Using if and else" hl_lines="1 4"
156+
if vec.ssize() > 100 {
157+
do_general_algorithm( container );
158+
}
159+
else {
160+
do_linear_scan( vec );
161+
}
162+
```
163+
82164

83165
## <a id="loops"></a> `#!cpp for`, `#!cpp while`, `#!cpp do` — Loops
84166

85-
TODO
167+
**`#!cpp do`** and **`#!cpp while`** are like always in C++, except that `(` `)` parentheses around the condition are not required. Instead, `{` `}` braces around the loop body *are* required.
168+
169+
**`#!cpp for range do (e)`** ***statement*** says "for each element in `range`, call it `e` and perform the statement." The loop parameter `(e)` is an ordinary parameter that can be passed isoing any [parameter passing style](#parameters); as always, the default is `in`, which is read-only and expresses a read-only loop. The statement is not required to be enclosed in braces.
170+
171+
Every loop can have a `next` clause, that is performed at the end of each loop body execution. This makes it easy to have a counter for any loop, including a range `#!cpp for` loop.
172+
173+
> Note: Whitespace is just a stylistic choice. This documentation's style generally puts each keyword on its own line and lines up what follows.
174+
175+
For example:
176+
177+
``` cpp title="Using loops" hl_lines="4 5 13 16 17 22-24"
178+
words: std::vector<std::string> = ("Adam", "Betty");
179+
i := 0;
180+
181+
while i < words.ssize() // while this condition is true
182+
next i++ // and increment i after each loop body is run
183+
{ // do this loop body
184+
std::cout << "word: (words[i])$\n";
185+
}
186+
// prints:
187+
// word: Adam
188+
// word: Betty
189+
190+
do { // do this loop body
191+
std::cout << "**\n";
192+
}
193+
next i-- // and decrement i after each loop body is run
194+
while i > 0; // while this condition is true
195+
// prints:
196+
// **
197+
// **
198+
199+
for words // for each element in 'words'
200+
next i++ // and increment i after each loop body is run
201+
do (inout word) // declare via 'inout' the loop can change the contents
202+
{ // do this loop body
203+
word = "[" + word + "]";
204+
std::cout << "counter: (i)$, word: (word)$\n";
205+
}
206+
// prints:
207+
// counter: 0, word: [Adam]
208+
// counter: 1, word: [Betty]
209+
```
210+
211+
There is no special "select" or "where" to perform the loop body for only a subset of matches, because this can naturally be expressed with `if`. For example:
212+
213+
``` cpp title="Using loops + if" hl_lines="7"
214+
// Continuing the previous example
215+
i = 0;
216+
217+
for words
218+
next i++
219+
do (word)
220+
if i % 2 == 1 // if i is odd
221+
{ // do this loop body
222+
std::cout << "counter: (i)$, word: (word)$\n";
223+
}
224+
// prints:
225+
// counter: 1, word: [Betty]
226+
```
227+
228+
229+
### Loop names, `#!cpp break`, and `#!cpp continue`
86230

87231
Loops can be named using the usual **name `:`** syntax that introduces all names, and `#!cpp break` and `#!cpp continue` can refer to those names. For example:
88232

89-
``` cpp title="Using named break and continue" hl_lines="6 10"
233+
``` cpp title="Using named break and continue" hl_lines="1 3 6 10"
90234
outer: while i<M next i++ { // loop named "outer"
91235
// ...
92236
inner: while j<N next j++ { // loop named "inner"

docs/cpp2/objects.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ Its declaration is written using the same **name `:` kind `=` value** [declarati
1313

1414
For example:
1515

16-
``` cpp title="Declaring some objects"
16+
``` cpp title="Declaring some objects" hl_lines="3 4 7-9 12 13"
1717
// numbers is an object of type std::vector<point2d>,
1818
// defined as having the initial contents 1, 2, 3
1919
numbers: std::vector<int> = (1, 2, 3);
@@ -23,6 +23,10 @@ numbers: std::vector = (1, 2, 3); // same, deducing the vector's type
2323
count: int = -1;
2424
count: _ = -1; // same, deducing the object's type with the _ wildcard
2525
count := -1; // same, deducing the object's type by just omitting it
26+
27+
// pi is a variable template; == signifies the value never changes (constexpr)
28+
pi: <T: type> T == 3.14159'26535'89793'23846L;
29+
pi: _ == 3.14159'26535'89793'23846L; // same, deducing the object's type
2630
```
2731

2832

docs/cpp2/types.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ test: (x: item, y: item) = {
268268
}
269269
```
270270

271-
The above is the same as in Cpp1 because most of Cpp2's `#!cpp operator<=>` feature has already been merged into ISO C++ (Cpp1). In additiona, in Cpp2 comparisons with the same precedence can be safely chained, and always have the mathematically sound transitive meaning or else are rejected at compile time:
271+
The above is the same as in Cpp1 because most of Cpp2's `#!cpp operator<=>` feature has already been merged into ISO C++ (Cpp1). In addition, in Cpp2 comparisons with the same precedence can be safely chained, and always have the mathematically sound transitive meaning or else are rejected at compile time:
272272

273273
- **Valid chains: All `<`/`<=`, all `>`/`>=`, or all `==`.** All mathematically sound and safe chains like `a <= b < c` are supported, with efficient single evaluation of each term. They are "sound" because they are transitive; these chains imply a relationship between `a` and `c` (in this case, the chain implies that `a <= c` is also true).
274274

docs/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ main: () = {
99

1010
## <a id="what-is-cpp2"></a> What is Cpp2?
1111

12-
"Cpp2", short for "C++ syntax 2", is my ([Herb Sutter's](https://github.com/hsutter)) personal project to try to make writing ordinary C++ types/functions/objects be much **simpler and safer**, without breaking backward compatibility. Bjarne Stroustrup said it best:
12+
"Cpp2," short for "C++ syntax 2," is my ([Herb Sutter's](https://github.com/hsutter)) personal project to try to make writing ordinary C++ types/functions/objects be much **simpler and safer**, without breaking backward compatibility. Bjarne Stroustrup said it best:
1313

1414
> "Inside C++, there is a much smaller and cleaner language struggling to get out." <br>&emsp;&emsp;— Bjarne Stroustrup, _The Design and Evolution of C++_ (D&E), 1994
1515
>

0 commit comments

Comments
 (0)