Syntax styles in programming languages

Up: Programming language design

This is a collection of different syntactical styles in programming languages. It’s woefully incomplete.

Directly relevant:

Other resources:

Comments

Single line comments:

Multi-line comments:

Multi-line comments might not really be that useful.

Indentation and bracing

Braces (C, Java, … style):

stuff {
  thing
}

doend (Ruby style):

stuff do
  thing
end

Explicitly naming the thing to end:

stuff
  thing
end stuff

There’s also bash’s bizarre way of ending statements, with reverse keywords like elihw and esac, but that’s just silly.

Calls

Regular math-style or C-style function calls:

cos(pi)
plus(2, 3)

Haskell-style calls (without parentheses):

cos pi
plus 2 3

OO-style calls (Smalltalk style):

pi cos
2 plus: 3

OO-style calls (Ruby style, i.e. no parentheses needed):

pi.cos
2.plus(3)

This approach makes it non-trivial to get hold of the method as an object.

OO-style calls (JavaScript style):

pi.cos()
2.plus(3)

Getting hold of the method itself is simple in this case.

The question of how the method is bound remains. If you get the method, is it still bound to the original object, or not? If not, how do you call it? Do you pass self as the first argument, perhaps?

Concatenative style (like Forth and Factor):

pi cos
2 3 plus

To do: Describe named parameters

Function definitions

No braces, implicit return:

fun greeting()
  "Hello!"
end

The parentheses, if no parameters are available, could be left out:

fun greeting
  "Hello!"
end

Braces, explicit return:

fun greet() {
  return "Hello!"
}

Braces, shorthand, implicit return:

fun greet() = "Hello!"

With positional parameters:

fun greet(name)
  return "Hello, ${name}"
end

With default value, assigned using = (Python style):

fun greet(name = "stranger")
  return "Hello, ${name}"
end

With default value, assigned using : (Ruby style):

fun greet(name: "stranger")
  return "Hello, ${name}"
end

Using : can conflict with type declarations.

Different parameter name when calling and inside function definition (like Swift):

func greet(person: String, from hometown: String) -> String {
  // use `hometown` here
}

greet(person: "Jake", from: "Berlin")

Positional parameters can be opt-in, using _:

func greet(_ person: String, from hometown: String) -> String {
  // use `hometown` here
}

greet("Jake", from: "Berlin")

To do: Describe variable-argument definitions

Pointers

Dereferencing in C, C++, …:

*pointer

Dereferencing in Zig:

pointer.*

In Swift, an argument can be passed in to be mutated by specifying inout on the parameter:

func play(game: inout Game) {
  // …
}

In C#, on the caller side, ref is used, but the parameter is the same:

Game Update(Game game, int guess) {
  // …
}
Play(ref game);

Method definitions

To do: associated functions

To do: instance variables

To do: self (this instance), e.g. this.doSomething()

To do: self (this type), e.g. new thisClass

Strings

Double-quoted:

"Hello"

With interpolation, Ruby style:

"Hello, #{name}"

With interpolation, Python style:

f"Hello, {name}"

With interpolation, JavaScript style:

`Hello, ${name}`

With interpolation, Swift style:

"Hello, \(name)"

Characters

Ruby: "a" or ?a — there is no difference between characters and strings.

Crystal: 'a'

Iteration

I’m leaving out old-school C-style for loop iteration because I think it’s too old-school.

Dedicated for construct:

for elem in iterable {
    // …
}

Function:

iterable.each(func (elem) {
    // …
})

Note that the above can’t use return to break out of the loop. Smalltalk-style non-local returns would allow that, but it’d return from the function entirely:

"a strange way to get the first element of an iterable"
iterable do: [:elem | ^elem]

Ruby has blocks and functions, and thus has nonlocal returns and can break out of blocks. Ruby also has a ton of custom iterators, like #times:

12.times do
  # …
end

Structs

C:

struct Something {
  Type myProp;
};

Crystal:

struct Point
  property x, y

  def initialize(@x : Int32, @y : Int32)
  end
end

Crystal has explicit initializer. The initializer syntax is nicer than Ruby’s, in that it removes some duplication by allowing params to be declared as ivars.

Zig:

const Point = struct {
    x: f32,
    y: f32,
};

const p = Point {
    .x = 0.12,
    .y = 0.34,
};

Using the same syntax for types (const Point = struct …) as for values (const p = Point …) is interesting. Maybe confusing, though?

Nim:

type
  Animal* = object
    name*, species*: string
    age: int

var carl: Animal
carl = Animal(name : "Carl",
              species : "L. glama",
              age : 12)

I like how types can be used as functions to construct instances.

Ranges

Equality

Ugh! This one is complicated. Common ways to check for equality are:

Different types of equality to take into account:

Ruby’s === is confusing (“case equality” is ambiguous and hard to explain).

See Equality in programming languages.

Discard return value

Some programming languages require a return value to be used, or error instead.

_ = myFunction()

C++ can use [[nodiscard]] to require a value to be used, but it’s opt-in:

[[nodiscard]]
int update() {
  // …
}

Nim uses the discard keyword:

discard myFunction()

Pure functions and constant expressions

GCC allows adding __attribute__((const)) to claim that a function is pure (even if it is not!):

__attribute__((const))
auto update(Game game, int guess) -> Game {
  // …
}

GCC also allows constexpr to mark something as being able to be executed at compile time:

constexpr
auto update(Game game, int guess) -> Game {
  // …
}

Rust supports const functions, which are also able to be executed at compile time:

const fn update(game: Game, guess: int32) -> Game {
  // …
}

Effects

Koka defines a handful of effect types, including:

Additionally, pure is an alias for exn and div.

More:

Note last edited August 2024.
Incoming links: Programming language design.