Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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.

LuxSwift
let x = 1 / var x = 1same
int, float, string, boolInt, 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 += 4queue.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), noneInt?, 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..nfor x in 0..<n
if, while, returnsame

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

PythonSwift
x = 1var x = 1, or let x = 1 if it never changes
def f(n): return n * 2func 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
Nonenil, and the type must be T?
x if c else yc ? 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) + 1d[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

JavaScriptSwift
let x = 1 / const x = 1var 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.lengthxs.count
xs.map(f), .filter, .reducesame names, same order
xs.push(v)xs.append(v)
{a: 1} as a bag of values["a": 1]
{a: 1} as a recordstruct with fields
for (const x of xs)for x in xs
null and undefinednil, once, and the type says so
x ?? yx ?? y
a?.b?.ca?.b?.c
=====, which compares values; === compares object identity
try / catch (e)do / catch , and calls are marked try
async function / awaitfunc ... async / await
Promise.all([a, b])async let a = ...; async let b = ...
JSON.stringify / JSON.parseJSONEncoder / 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.