Skip to content
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

Updating function and variable docs #1017

Merged
merged 18 commits into from
Jan 28, 2022
Merged
18 changes: 8 additions & 10 deletions docs/design/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -301,23 +301,21 @@ Some common expressions in Carbon include:
### Functions

> References: [Functions](functions.md)
>
> **TODO:** References need to be evolved.

Functions are the core unit of behavior. For example:

```carbon
fn Sum(a: Int, b: Int) -> Int;
fn Add(a: i64, b: i64) -> i64;
```

Breaking this apart:

- `fn` is the keyword used to indicate a function.
- Its name is `Sum`.
- It accepts two `Int` parameters, `a` and `b`.
- It returns an `Int` result.
- Its name is `Add`.
- It accepts two `i64` parameters, `a` and `b`.
- It returns an `i64` result.

You would call this function like `Sum(1, 2)`.
You would call this function like `Add(1, 2)`.

### Blocks and statements

Expand Down Expand Up @@ -354,16 +352,16 @@ work similarly to function parameters.
For example:

```carbon
fn Foo() {
var x: Int = 42;
fn DoSomething() {
var x: i64 = 42;
}
```

Breaking this apart:

- `var` is the keyword used to indicate a variable.
- Its name is `x`.
- Its type is `Int`.
- Its type is `i64`.
- It is initialized with the value `42`.

### Lifetime and move semantics
Expand Down
179 changes: 143 additions & 36 deletions docs/design/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,54 +10,161 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

## Table of contents

- [TODO](#todo)
- [Basic functions](#basic-functions)
- [Overview](#overview)
- [Function definitions](#function-definitions)
- [Return clause](#return-clause)
- [`return` statements](#return-statements)
- [Function declarations](#function-declarations)
- [Function calls](#function-calls)
- [Functions in other features](#functions-in-other-features)
- [Alternatives considered](#alternatives-considered)
- [References](#references)

<!-- tocstop -->

## TODO
## Overview

This is a skeletal design, added to support [the overview](README.md). It should
not be treated as accepted by the core team; rather, it is a placeholder until
we have more time to examine this detail. Please feel welcome to rewrite and
update as appropriate.
Functions are the core building block for applications. Carbon's basic function
syntax is:

## Basic functions
- _parameter_: _identifier_ `:` _expression_
- _parameter-list_: _[ parameter_ `,` _parameter_ `,` _... ]_
- _return-clause_: _[_ `->` _< expression |_ `auto` _> ]_
- _signature_: `fn` _identifier_ `(` _parameter-list_ `)` _return-clause_
- _function-definition_: _signature_ `{` _statements_ `}`
- _function-declaration_: _signature_ `;`
- _function-call_: _identifier_ `(` _[ expression_ `,` _expression_ `,` _...
]_ `)`

Programs written in Carbon, much like those written in other languages, are
primarily divided up into "functions" (or "procedures", "subroutines", or
"subprograms"). These are the core unit of behavior for the programming
language. Let's look at a simple example to understand how these work:
A function with only a signature and no body is a function declaration, or
forward declaration. When the body is a present, it's a function definition. The
body introduces nested scopes which may contain local variable declarations.

## Function definitions

A basic function definition may look like:

```carbon
fn Add(a: i64, b: i64) -> i64 {
return a + b;
}
```
fn Sum(a: Int, b: Int) -> Int;

This declares a function called `Add` which accepts two `i64` parameters, the
first called `a` and the second called `b`, and returns an `i64` result. It
returns the result of adding the two arguments.

C++ might declare the same thing:

```cpp
std::int64_t Add(std::int64_t a, std::int64_t b) {
return a + b;
}

// Or with trailing return type syntax:
auto Add(std::int64_t a, std::int64_t b) -> std::int64_t {
return a + b;
}
```

This declares a function called `Sum` which accepts two `Int` parameters, the
first called `a` and the second called `b`, and returns an `Int` result. C++
might declare the same thing:
### Return clause

The return clause of a function specifies the return type using one of three
possible syntaxes:

- `->` followed by an _expression_, such as `i64`, directly states the return
type. This expression will be evaluated at compile-time, so must be valid in
that context.
- For example, `fn ToString(val: i64) -> String;` has a return type of
`String`.
- `->` followed by the `auto` keyword indicates that
[type inference](type_inference.md) should be used to determine the return
type.
- For example, `fn Echo(val: i64) -> auto { return val; }` will have a
return type of `i64` through type inference.
- Declarations must have a known return type, so `auto` is not valid.
- The function must have precisely one `return` statement. That `return`
statement's expression will then be used for type inference.
- Omission indicates that the return type is the empty tuple, `()`.
- For example, `fn Sleep(seconds: i64);` is similar to
`fn Sleep(seconds: i64) -> ();`.
- `()` is similar to a `void` return type in C++.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps state that switching between fn F(...) and fn F(...) -> () is a compatible change for all callers? (though the return statements in the function's definition would have to be updated)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest proposing this separately. There are two reasons I could see this not being true:

  1. It could be decided that var x: auto = F() is either required or disallowed in some cases.
  2. Constraints could be allowed to detect the difference.

In any case, I think this is a non-trivial decision and may be more appropriate for a proposal or its own change (this doc edit has already taken longer than I expected, I'd rather not push it out another couple weeks).

### `return` statements

The [`return` statement](control_flow/return.md) is essential to function
control flow. It ends the flow of the function and returns execution to the
caller.

When the [return clause](#return-clause) is omitted, the `return` statement has
no expression argument, and function control flow implicitly ends after the last
statement in the function's body as if `return;` were present.

When the return clause is provided, including when it is `-> ()`, the `return`
statement must have an expression that is convertible to the return type, and a
`return` statement must be used to end control flow of the function.

## Function declarations

Functions may be declared separate from the definition by providing only a
signature, with no body. This provides an API which may be called. For example:

```carbon
// Declaration:
fn Add(a: i64, b: i64) -> i64;

// Definition:
fn Add(a: i64, b: i64) -> i64 {
return a + b;
}
```
std::int64_t Sum(std::int64_t a, std::int64_t b);

// Or with trailing return type syntax:
auto Sum(std::int64_t a, std::int64_t b) -> std::int64_t;
The corresponding definition may be provided later in the same file or, when the
declaration is in an
[`api` file of a library](code_and_name_organization/#libraries), in the `impl`
file of the same library. The signature of a function declaration must match the
corresponding definition. This includes the [return clause](#return-clause);
even though an omitted return type has equivalent behavior to `-> ()`, the
presence or omission must match.

## Function calls

Function calls use a function's identifier to pass multiple expression arguments
corresponding to the function signature's parameters. For example:

```carbon
fn Add(a: i64, b: i64) -> i64 {
return a + b;
}

fn Main() {
Add(1, 2);
}
```

Let's look at how some specific parts of this work. The function declaration is
introduced with a keyword `fn` followed by the name of the function `Sum`. This
declares that name in the surrounding scope and opens up a new scope for this
function. We declare the first parameter as `Int a`. The `Int` part is an
expression (here referring to a constant) that computes the type of the
parameter. The `:` marks the end of the type expression and introduces the
identifier for the parameter, `a`. The parameter names are introduced into the
function's scope and can be referenced immediately after they are introduced.
The return type is indicated with `-> Int`, where again `Int` is just an
expression computing the desired type. The return type can be completely omitted
in the case of functions which do not return a value.

Calling functions involves a new form of expression: `Sum(1, 2)` for example.
The first part, `Sum`, is an expression referring to the name of the function.
The second part, `(1, 2)` is a parenthesized list of arguments to the function.
The juxtaposition of one expression with parentheses forms the core of a call
expression, similar to a postfix operator.
Here, `Add(1, 2)` is a function call expression. `Add` refers to the function
definition's identifier. The parenthesized arguments `1` and `2` are passed to
the `a` and `b` parameters of `Add`.

## Functions in other features

Other designs build upon basic function syntax to add advanced features:

- [Generic functions](generics/overview.md#generic-functions) adds support for
deduced parameters and generic type parameters.
- [Class member functions](classes.md#member-functions) adds support for
methods and class functions.

chandlerc marked this conversation as resolved.
Show resolved Hide resolved
## Alternatives considered

- [Function keyword](/proposals/p0438.md#function-keyword)
- [Only allow `auto` return types if parameters are generic](/proposals/p0826.md#only-allow-auto-return-types-if-parameters-are-generic)
- [Provide alternate function syntax for concise return type inference](/proposals/p0826.md#provide-alternate-function-syntax-for-concise-return-type-inference)
- [Allow separate declaration and definition](/proposals/p0826.md#allow-separate-declaration-and-definition)

## References

- Proposal
[#438: Add statement syntax for function declarations](https://github.com/carbon-language/carbon-lang/pull/438)
- Proposal
[#826: Function return type inference](https://github.com/carbon-language/carbon-lang/pull/826)
53 changes: 53 additions & 0 deletions docs/design/type_inference.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Type inference

<!--
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 -->

## Table of contents

- [Overview](#overview)
- [Open questions](#open-questions)
- [Inferring a variable type from literals](#inferring-a-variable-type-from-literals)
- [Alternatives considered](#alternatives-considered)
- [References](#references)

<!-- tocstop -->

## Overview
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zygoloid to ensure we're doing a decent job of introducing type inference and not missing any implications or connections to other parts of the language that we should establish...


[Type inference](https://en.wikipedia.org/wiki/Type_inference) occurs in Carbon
when the `auto` keyword is used. This may occur in
[variable declarations](variables.md) or [function declarations](functions.md).

At present, type inference is very simple: given the expression which generates
the value to be used for type inference, the inferred type is the precise type
of that expression. For example, the inferred type for `auto` in
`fn Foo(x: i64) -> auto { return x; }` is `i64`.

Type inference is currently supported for [function return types](functions.md)
and [declared variable types](variables.md).

## Open questions

### Inferring a variable type from literals

Using the type on the right side for `var y: auto = 1` currently results in a
constant `IntLiteral(1)` value, whereas most languages would suggest a variable
integer type, such as `i64`. Carbon might also make it an error. Although type
inference currently only addresses `auto` for variables and function return
types, this is something that will be considered as part of type inference in
general, because it also affects generics, templates, lambdas, and return types.

## Alternatives considered

- [Use `_` instead of `auto`](/proposals/p0851.md#use-_-instead-of-auto)

## References

- Proposal
[#851: auto keyword for vars](https://github.com/carbon-language/carbon-lang/pull/851)
10 changes: 8 additions & 2 deletions docs/design/variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

Carbon's local variable syntax is:

> `var` _identifier_`:` _type_ _[_ `=` _value_ _]_`;`
- `var` _identifier_`:` _< expression |_ `auto` _> [_ `=` _value ]_`;`

Blocks introduce nested scopes and can contain local variable declarations that
work similarly to function parameters.
Expand All @@ -31,7 +31,7 @@ For example:

```
fn Foo() {
var x: Int = 42;
var x: i32 = 42;
}
```

Expand All @@ -40,6 +40,9 @@ type `Int` and is initialized with the value `42`. These variable declarations
(and function declarations) have a lot more power than what we're covering just
yet, but this gives you the basic idea.

If `auto` is used in place of the type, [type inference](type_inference.md) is
used to automatically determine the variable's type.

While there can be global constants, there are no global variables.

## Notes
Expand All @@ -61,10 +64,13 @@ discover that their convenience outweighs any improvements afforded.
- [Colon between type and identifier](/proposals/p0339.md#colon-between-type-and-identifier)
- [Type elision](/proposals/p0339.md#type-elision)
- [Type ordering](/proposals/p0618.md#type-ordering)
- [Elide the type instead of using `auto`](/proposals/p0851.md#elide-the-type-instead-of-using-auto)

## References

- Proposal
[#339: `var` statement](https://github.com/carbon-language/carbon-lang/pull/339)
- Proposal
[#618: `var` ordering](https://github.com/carbon-language/carbon-lang/pull/618)
- Proposal
[#851: auto keyword for vars](https://github.com/carbon-language/carbon-lang/pull/851)