1 of 54

Carbon Language

Syntax and trade-offs

Jon Ross-Perkins

github.com/JonMeow

2 of 54

3 of 54

Successor language

  • C → C++

4 of 54

Successor language

  • C → C++
  • JavaScript → TypeScript

5 of 54

Successor language

  • C → C++
  • JavaScript → TypeScript
  • Objective-C → Swift

6 of 54

Successor language

  • C → C++
  • JavaScript → TypeScript
  • Objective-C → Swift
  • Java → Kotlin

7 of 54

Successor language

  • C → C++
  • JavaScript → TypeScript
  • Objective-C → Swift
  • Java → Kotlin
  • C++ → ???

8 of 54

🧪🧪🧪�UNDER�CONSTRUCTION

9 of 54

Language goals

  • Performance-critical software
  • Software and language evolution
  • Code that is easy to read, understand, and write
  • Practical safety and testing mechanisms
  • Fast and scalable development
  • Modern OS platforms, hardware architectures, and environments
  • Interoperability with and migration from existing C++ code

10 of 54

Project goals

  • Community and culture
  • Language tools and ecosystem

11 of 54

Between friends, there is room for disagreement.

Haim Saban

12 of 54

Project goals

  • Community and culture
  • Language tools and ecosystem

13 of 54

C++ tooling

  • clang or gcc
  • clang-format
  • clang-tidy
  • clangd

14 of 54

Carbon tooling

  • Compiler (clang)
  • Formatter (clang-format)
  • Automatic language upgrades (clang-tidy-like)
  • IDE and LSP support (clangd + extra configs)
  • Refactoring support (AST matchers)
  • Package manager

15 of 54

Tools require context

16 of 54

int a = 0, b = 1;

17 of 54

for (int i = 0; i < n; ++i) {

18 of 54

int c = a + b;

19 of 54

std::cout << b << "\n";

20 of 54

a = b;

21 of 54

b = c;

22 of 54

}

23 of 54

24 of 54

void Fibonacci(int n) {

int a = 0, b = 1;

for (int i = 0; i < n; ++i) {

int c = a + b;

std::cout << b << "\n";

a = b;

b = c;

}

}

25 of 54

What is it?

printer<circle>(radius);

26 of 54

It's a...

  • Function call!

struct circle {};

template<typename T>

void printer(int radius) {}

auto main() -> int {

int radius = 3;

printer<circle>(radius);

return 0;

}

27 of 54

It's a...

  • Function call!
  • Constructor call!

#define radius 3

struct circle {};

template<typename T>

struct printer {

printer(float r) {}

};

auto main() -> int {

printer<circle>(radius);

return 0;

}

28 of 54

It's a...

  • Function call!
  • Constructor call!
  • Variable declaration!

constexpr int radius = 3;

struct circle {};

template<typename T>

struct printer {

void print() {}

};

auto main() -> int {

printer<circle>(radius);

radius.print();

return 0;

}

29 of 54

It's a...

  • Function call!
  • Constructor call!
  • Variable declaration!
  • Comparison!

auto main() -> int {

int printer = 1;

int circle = 2;

int radius = 3;

printer<circle>(radius);

return 0;

}

30 of 54

Context is king

31 of 54

Context is king

But should it be?

32 of 54

Why does context matter?

  • Context is expensive for both developers and compilers
  • Name lookup results affect code behavior
  • Requires looking into #includes (or imports)
  • Requires parsing, type checking, and other semantic checks

33 of 54

Why minimize context?

  • Easier for developers to quickly read behavior
  • Faster for compilers to parse code
  • Easier to provide good errors
  • Easier for IDEs to parse and highlight
  • Makes syntax less like C++

34 of 54

Common considerations

  • Minimizing context and lookahead
  • Improving understandability
  • What are other languages doing?

35 of 54

Want a:

  • Function call?
  • Constructor call?
  • Variable declaration?
  • Comparison?

Printer(Circle, radius);

Printer(Circle).Make(radius);

var radius: Printer(Circle);

printer < circle and circle > (radius);

36 of 54

Introducer keywords

37 of 54

var radius: Printer(Circle);

class Circle

class Printer(template t:! Type)

fn Draw()

interface Shape

let Pi: f32

38 of 54

Trade-offs

  • Longer declarations
  • Less ambiguity in syntax
  • Easier to parse code
  • Consistency in declarations

39 of 54

Templates

40 of 54

fn Printer(template T:! Type, radius: i32);

// Call:

Printer(Circle, radius);

class Printer(template T:! Type) {

fn Make(radius: i32) -> Printer(T);

}

// Construct instance:

Printer(Circle).Make(radius);

41 of 54

Trade-offs

  • Losing familiarity of <>
  • Less ambiguous with comparison operators
  • Gaining consistency with ()
  • Easy to mix-and-match with Carbon's typed generics
  • Implies types are expressions

42 of 54

auto Circle() -> int { return 0; }

struct Circle {};

auto main() -> int {

auto c = Circle();

}

43 of 54

auto Circle() -> int { return 0; }

struct Circle {};

template <typename T>

class Printer {};

auto main() -> int {

auto f = Circle{};

auto p = Printer<Circle>();

auto c = (struct Circle){};

}

44 of 54

Operator associativity

45 of 54

// C++

printer<circle>(radius);

// Carbon

printer < circle and circle > radius;

// or

(if (printer < circle) then 1 else 0) > radius;

46 of 54

C++

Carbon

47 of 54

Trade-offs

  • Increases expression verbosity (more parentheses)
  • Increases friction for developers familiar with C++ precedence
  • Reduces error rate with unfamiliar associativity

48 of 54

Packages and namespaces

49 of 54

package Sample api;

import Geometry;

namespace Draw;

fn Draw.Circle(c: Geometry.Circle) {}

50 of 54

Trade-offs

  • No more name collisions in the global namespace
    • C++ interop will likely need global namespace support
  • Deeper namespacing is more verbose

51 of 54

Tools require context

52 of 54

Between friends, there is room for disagreement.

Haim Saban

53 of 54

Jon Ross-Perkins

github.com/JonMeow

Thank you!

carbon.compiler-explorer.com

github.com/carbon-language-carbon-lang

54 of 54

#carbonlang