This is a collection of different syntactical styles in programming languages. It’s woefully incomplete.
Directly relevant:
Other resources:
Single line comments:
// abc
(C, Java, …)# abc
(Ruby, bash, …); abc
(Lisp family, …)% abc
(TeX)-- abc
(Lua)Multi-line comments:
/* abc */
(C, Java, …)(* abc *)
(Pascal)"abc"
(Smalltalk)Multi-line comments might not really be that useful.
Braces (C, Java, … style):
stuff {
thing
}
do
–end
(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.
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
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
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);
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
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)"
Ruby: "a"
or ?a
— there is no difference between characters and strings.
Crystal: 'a'
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
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.
1..100
1..=100
1...100
range(1, 100)
Ugh! This one is complicated. Common ways to check for equality are:
==
===
====
equals()
(Java) or eql?()
(Ruby)equal?()
(in Ruby: identity, i.e. same object)Different types of equality to take into account:
1.0
and 1
)Ruby’s ===
is confusing (“case equality” is ambiguous and hard to explain).
See Equality in programming languages.
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()
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 {
// …
}
Koka defines a handful of effect types, including:
total
(mathematically total function)exn
(may raise an exception)div
(may not terminate, i.e. may diverge)console
(may write to console)ndet
(non-deterministic)Additionally, pure
is an alias for exn
and div
.
More:
alloc<h>
blocking
fsys
handled
handled1
local
net
read
write
local
scope
ui