Functions

Pull request

Table of contents

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-C
  • def: Python and Ruby
  • fn: Rust
  • fun: Kotlin
  • func: Go and Swift
  • function: 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:

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”.

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.
  • 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:

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 than func. Both are shorter than “function”.
  • Consistency with other abbreviations:
    • We are using abbreviations like var, and something like mut or const seems likely. ptr is possible.
      • func is consistent with abbreviation by removing letters at the end.
      • fn is more consistent with abbreviations like ptr that remove letters in the middle, but a three letter abbreviation like fcn 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.
  • 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 with fn for function pointers.
      • It’s also used for the Fn key, although assumptions about the meaning of “Fn” as abbreviation for “function” versus “F-number key” (as in F5) may mislead some developers.
      • fn is difficult to search for.
  • 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.