Functions
Table of contents
- Problem
- Background
- Proposal
- Rationale based on Carbon’s goals
- Open questions
- Alternatives considered
Problem
We currently have placeholder guidance on functions. The intent of this proposal is to establish agreement on the basics, providing a baseline for future evolution.
Background
C++ syntax
C++ syntax for function declarations comes in two forms:
std::int64_t Sum(std::int64_t a, std::int64_t b) {
return a + b;
}
// Or with trailing return type syntax:
auto Sum(std::int64_t a, std::int64_t b) -> std::int64_t {
return a + b;
}
Bodies are always wrapped by braces.
Other languages
To summarize keyword use from other languages:
- Type: C# and Java
-
: Objective-Cdef
: Python and Rubyfn
: Rustfun
: Kotlinfunc
: Go and Swiftfunction
: JavaScript, MatLab, PHP, R, and TypeScript
For exhaustive function examples:
-
C#
int Sum(int a, int b) { return a + b; }
-
Go
func add(a int, b int) int { return a + b }
-
Java
Int Sum(Int a, Int b) { return a + b; }
-
JavaScript
function Sum(a, b) { return a + b; }
-
Kotlin
fun add(a: Int, b: Int): Int { return a + b }
-
Matlab
function s = sum(a,b) s = a+b; end
-
Objective-C
- (int)sum:(int)a (int)b { return a + b; }
-
PHP
function sum(int $a, int $b) { return $a + $b; }
-
Python
def sum(a, b): return a + b def sum(a: int, b: int) -> int: return a + b
-
R
sum <- function(a, b) { a + b }
-
Ruby
def sum(a, b) return a + b end
-
Rust
fn sum(a: i64, b: i64) -> i64 { a + b }
-
Swift
func sum(a: Int, b: Int) -> Int { return a + b }
-
TypeScript
function sum(a: number, b: number): number { return a + b; }
Forward declarations
Forward declarations of functions are largely unique to C++. Objective-C also has this. However, most other languages being discussed do not.
Proposal
Functions
Function declarations and definitions should look like:
fn
function name (
type identifier [ ,
type identifier ]… )
[ ->
return type ] [ {
body }
| ;
]
Arguments should be comma-separated and imitate var
syntax, although var
itself is not used.
For example:
fn Sum(Int a, Int b) -> Int {
<body>
}
fn UnusedArgument(Int _) -> Int {
<body>
}
The return type may optionally be omitted if ()
. For example, these two function declarations both return ()
:
fn Print(String s) -> ();
fn Print(String s);
Forward declarations
Forward declarations will be supported in order to support separation of API and implementation, as explained in code and name organization. For example:
package Math api;
fn Sum(Int a, Int b) -> Int;
Forward declarations will experimentally only be allowed in api
files, and only when the definition is in the library’s impl
file. The intent is to minimize use, consistent with most other languages that have no forward declaration support at all.
Rationale based on Carbon’s goals
Carbon needs functions in order to be writable by developers. That functionality needs a syntax.
Relevant goals are:
-
3. Code that is easy to read, understand, and write:
- Adding a keyword makes it easy for developers to visually identify functions.
- Trailing return syntax should be easier to understand than C++’s older preceding return syntax. Only allowing one avoids requiring developers to recognize and choose between equivalent syntaxes.
-
5. Fast and scalable development: The addition of a keyword should make parsing easy.
-
7. Interoperability with and migration from existing C++ code: Keeping syntax close to C++ will make it easier for developers to transition.
Open questions
Calling functions defined later in the same file
C++ supports forward declaring functions in a .cpp
file, and this may be useful for handling interdependencies between functions. In Carbon, we should instead aim to allow having a function able to call a function defined later in the same file without requiring a forward declaration.
Advantages:
- Minimize accidents with forward declarations not matching implementation.
- Fewer forward declarations for developers to find while reading.
Disadvantages:
- Shifts burden onto how code is parsed and executed.
We’ll need to evaluate how this works. There is tracked by #472. This does not need to block this proposal, as we can allow it later if that’s the decision; it may also be helpful to give more time to consider how name lookup should work.
Optional argument names
Argument names are required under this proposal. It’s likely that developers will want a way to indicate unused arguments. This is tracked by #476.
Alternatives considered
Function keyword
We may have anchored on fn
without much consideration. A key piece of this proposal is to suggest reconsidering the choice, even if we end up with the same.
Type
Advantages:
- Echoes C++, C#, and Java.
- Simpler to write; a type will often be present for the return regardless.
Disadvantages:
- Makes function syntax more difficult to parse.
- In C++, it’s notable that functions with a trailing return still start with
auto
to indicate a “type”.
- In C++, it’s notable that functions with a trailing return still start with
There’s a consensus that some keyword should be used in order to simplify parsing, so this option is not expected to receive much support.
-
(dash)
Advantages:
- Echoes Objective-C instance method syntax.
- Class methods use
+
and we could echo that too.
- Class methods use
- This is the most succinct option.
Disadvantages:
- Easy to miss, and hard to read as a result.
- Might be ambiguous with the
-
operator.
Although this alternative is mentioned, it is not expected to receive much support due to its readability problem.
def
Advantages:
- Echoes Python and Ruby.
Disadvantages:
def
is presumably short for “define”, which may not be as obvious as a “function” derivative.
fn
Advantages:
- Echoes Rust.
- The shortest “function” derivative.
- Likely comes from the fn key.
Disadvantages:
- Abbreviation by removing letters in the middle of a word is disallowed by Carbon’s C++ style.
- Even though the abbreviation is in Wikipedia, it is not associated with functions in programming.
- Would likely be the only keyword abbreviated this way.
fun
Advantages:
- Echoes Kotlin.
- Could be used with
var
as a push towards three letter abbreviations.- Not clear there are other examples of three letter abbreviations.
let
may be used in a similar way, although it’s not an abbreviation.
Disadvantages:
- “fun” is a common English word and may be a mildly confusing choice as a result.
-
When wrapping function definitions, the function name and wrapped types would end up on the same column when indenting by 4 spaces. These use the same casing, so it may make it slower to understand code.
-
For example:
fun Print( String message) { ... } // Similar confusion if the body of the function is indented 4 spaces. fun Print(String message) { SomeFunctionCall(); ... }
-
This is also true for
var
, but wrapping arguments is expected to be more common for functions, and the casing more likely to match.
-
func
Advantages:
- Echoes Go and Swift.
Disadvantages:
- The longest abbreviated form of “function”.
function
Advantages:
- Echoes JavaScript, MatLab, PHP, R, and TypeScript.
- Clear meaning without any abbreviation.
Disadvantages:
- The longest “function” derivative.
Conclusion
fn
and func
are the favored options:
- Desire to abbreviate “function”:
fn
is clearly shorter thanfunc
. Both are shorter than “function”.
- Consistency with other abbreviations:
- We are using abbreviations like
var
, and something likemut
orconst
seems likely.ptr
is possible.func
is consistent with abbreviation by removing letters at the end.fn
is more consistent with abbreviations likeptr
that remove letters in the middle, but a three letter abbreviation likefcn
would be more consistent.
- We are using Google C++ style as a base, which explicitly discourages abbreviating by deleting letters within a word.
fn
deletes letters in the middle of “function”,func
does not.
- We are using abbreviations like
- Familiarity for developers:
func
is used by Go and Swift, both of which are common languages.func
is very searchable.
fn
is only used by Rust, which by most measures has less adoption than either Go or Swift at present. However, it is used in Hungarian notation, so some C++ developers will be familiar withfn
for function pointers.
- Pronunciation:
- Both may be pronounced as “function”, but may also be pronounced by their abbreviation.
func
could be as “funk”, defined as a music genre or state of depression.fn
could be as:- “eff en”, which has no direct meaning but could be misheard as “effing”, or profanity. However, it’s not clear that this potential mishearing is a pragmatic issue.
- “fun”, defined as enjoyment.
Note that both are argued here as reasonable solutions that would satisfy many. This was asked on #463. We are using fn
.