Appendix B: Coming From Another Language
Three translation tables, each followed by the traps that table does not show. Read the one you arrived from.
From Lux
Lux was built as the centroid of Rust, Swift and Go, so the ideas line up
directly and mostly what changes is spelling. lux convert swift will translate
a program you already understand, and reading its output beside your original is
the fastest version of this appendix.
| Lux | Swift |
|---|---|
let x = 1 / var x = 1 | same |
int, float, string, bool | Int, Double, String, Bool |
func f(n: int) -> int { } | func f(n: Int) -> Int { } |
f(n: 3) | f(n: 3) |
print(a, b) | print(a, b) |
"Score: " + string(42) | "Score: \(42)" |
length(s) / length(a) | s.count / a.count |
[int], primes[0] | [Int], primes[0] |
queue += 4 | queue.append(4) |
struct Point { x: int } | struct Point { var x: Int } |
Point(x: 3, y: 4) | Point(x: 3, y: 4) |
enum Shape { circle(radius: float) } | enum Shape { case circle(radius: Double) } |
Shape.circle(radius: 2.0) | .circle(radius: 2.0) |
match s { circle(let r) => ... } | switch s { case .circle(let r): ... } |
_ => | default: |
Option<int>, some(x), none | Int?, x, nil |
match o { some(let x) => ...; none => ... } | if let x = o { ... } else { ... } |
Result<int, string>, ok(x), err(e) | throws -> Int, return x, throw e |
for x in 0..n | for x in 0..<n |
if, while, return | same |
Four things will surprise you.
Strings interpolate rather than concatenate. "a" + string(n) is legal Swift
only after the conversion, and nobody writes it; "a\(n)" is the idiom, and it
takes any expression.
Errors are thrown rather than returned. Lux’s Result is a value you match,
and Swift has Result too, but the normal way to write a fallible function is
throws, with the error traveling up on its own until somebody catches it.
Chapter 9 covers both and says when each is right. Lux’s rule that a Result
may not be stored has no counterpart here, because Swift’s Result is an
ordinary value.
Swift has classes, and Lux deliberately does not. Chapter 7 is the chapter with
no Lux equivalent, and its argument is that you should keep using struct,
which is the only thing Lux let you do, until you meet a reason not to.
Swift’s switch on an enum case needs the leading dot and the case keyword,
and arms end at the next case rather than falling through. The exhaustiveness
rule is the one you already know, and it is enforced the same way.
From Python
| Python | Swift |
|---|---|
x = 1 | var x = 1, or let x = 1 if it never changes |
def f(n): return n * 2 | func f(_ n: Int) -> Int { n * 2 } |
f(3) | f(3), but labels appear unless declared _ |
f"{name} is {age}" | "\(name) is \(age)" |
len(x) | x.count |
[1, 2, 3] | [1, 2, 3] |
{"a": 1} | ["a": 1] |
{1, 2} | Set([1, 2]) |
for x in xs: | for x in xs { |
for i, x in enumerate(xs): | for (i, x) in xs.enumerated() { |
if x: | if x != 0, if !x.isEmpty, if x != nil |
None | nil, and the type must be T? |
x if c else y | c ? x : y |
[f(x) for x in xs if p(x)] | xs.filter(p).map(f) |
try: / except E as e: | do { / catch let e as E { |
raise E() | throw E() |
with open(p) as f: | defer { }, or a closure-taking API |
class C: | struct C first; class C when identity matters |
d.get(k, 0) | d[k] ?? 0 |
d[k] = d.get(k, 0) + 1 | d[k, default: 0] += 1 |
The three that will cost you an afternoon each.
Everything has a type and the type is fixed. A variable that holds an Int
holds an Int forever, a list holds one kind of thing, and a function’s return
type is the same on every path. What feels like a restriction in the first hour
is the thing that removes the test you would otherwise have to write.
There is no truthiness and no None hiding in a variable of ordinary type. A
value that might be missing has a different type, T?, and the compiler makes
you handle the empty case at the point where it might be empty. Chapter 5 is the
one to read twice.
Assignment copies. b = a for a list, a dictionary, or an instance of a
struct gives you a second one, and changing b leaves a alone. This is the
opposite of Python and it removes the aliasing bugs that copy.deepcopy exists
to paper over. Chapter 7 explains why it is affordable.
From JavaScript
| JavaScript | Swift |
|---|---|
let x = 1 / const x = 1 | var x = 1 / let x = 1 |
function f(n) { return n * 2 } | func f(_ n: Int) -> Int { n * 2 } |
(n) => n * 2 | { n in n * 2 } or { $0 * 2 } |
`${name} is ${age}` | "\(name) is \(age)" |
xs.length | xs.count |
xs.map(f), .filter, .reduce | same names, same order |
xs.push(v) | xs.append(v) |
{a: 1} as a bag of values | ["a": 1] |
{a: 1} as a record | struct with fields |
for (const x of xs) | for x in xs |
null and undefined | nil, once, and the type says so |
x ?? y | x ?? y |
a?.b?.c | a?.b?.c |
=== | ==, which compares values; === compares object identity |
try / catch (e) | do / catch , and calls are marked try |
async function / await | func ... async / await |
Promise.all([a, b]) | async let a = ...; async let b = ... |
JSON.stringify / JSON.parse | JSONEncoder / JSONDecoder with Codable |
class C {} | struct C first; class C when identity matters |
The traps are the same three as Python’s, plus one of your own.
== in Swift is not === in JavaScript. It compares contents, so two separately
built structs with equal fields are equal, and there is no coercion to worry
about because there is no coercion anywhere. When you want to ask whether two
class references point at the same object, === is the operator, and it does
not apply to structs at all.
Numbers are not all the same type. JavaScript has one number; Swift has Int
and Double and will not mix them without being told. Division of two Ints
truncates, which is the first thing that will surprise you and is covered in
Chapter 2.
The Shape That Does Not Translate
Every table above stops at the same place. Swift asks you to decide, for each type you define, whether its values are copied or shared, and neither Lux nor Python nor JavaScript asks that question. It is not syntax and there is no row for it.
Chapter 7 is that chapter. Read it slowly, write both versions of something small, and watch what changes. Everything else in this book is vocabulary you already had.