PONY λ M2 Modula-2
for Swift programmers

You already know Swift.Now explore other languages.

Side-by-side, interactive cheatsheets for Swift programmers
comparing Swift to other languages. Every example runs live in your browser — no setup, no installation.

▶ Start with Go Browse comparisons ↓

Choose your own path by reordering languages

Go Pre-Alpha

A deliberately smaller language, and a different bet on concurrency. No optionals, no enums with payloads, no exceptions, no inheritance, no generics until 1.18 — every absence is a design decision. What you get back is a language you can hold in your head, a compiler that finishes instantly, and goroutines and channels: cheap threads over shared memory, where blocking is free and no function is async.

  • The zero value replaces the optional: a missing map key is 0, a missing name is "", and the type system cannot tell "absent" from "empty"
  • Errors are values returned alongside the result, so if err != nil is one line in three — the failure path is as visible as the happy one, which is exactly the point
  • Interface conformance is implicit: the consumer defines the interface it needs, after the fact, and any type with matching methods satisfies it
  • No enums with payloads and no exhaustiveness — a sum type becomes an interface plus a struct per case, and a missing case falls silently into default
  • Slices alias: second := first shares the backing array, so a write through one is visible through the other (Swift's copy-on-write makes a copy a copy)
  • No actors and no Sendable: you take a sync.Mutex by hand, and a forgotten lock is found by the runtime race detector rather than the compiler
Python Beta ⚡ Works Offline ⚡ Offline

Every compile-time guarantee, traded for speed of writing. No compiler, no optionals, no value types, and type hints that look exactly like Swift annotations and are enforced by nothing at run time. What you get back is the fastest path from idea to working code, and a library for everything.

  • Type hints are not checked at run timedouble("nope") happily returns "nopenope". A separate tool (mypy/pyright) is the compiler you no longer have; turn it on day one
  • No optionals: None is a value nothing makes you check, and AttributeError: 'NoneType' object has no attribute … is the nil-crash, arriving in production
  • There are no value types — assignment always aliases, and a mutable default argument is created once and shared across every call
  • Duck typing replaces protocols: if it has the method it works, and typing.Protocol gives a static checker the structural conformance back
  • match destructures classes, dicts and lists (going beyond switch) — but has no exhaustiveness, so a missing case falls silently into case _
  • The GIL means threads give you no parallelism: asyncio for waiting, multiprocessing for computing, threads almost never
Kotlin Pre-Alpha

The platform swap, and a language that rhymes almost too well. Designed a year apart and borrowing from each other ever since: null safety with ?, data classes, sealed hierarchies, when/switch, extensions, named arguments with defaults, trailing lambdas, structured concurrency. The risk here is complacency — so this page is about the places the rhyme misleads.

  • A data class is not a struct: it is a reference, so second = first aliases and copy() is a method you must remember (and it is shallow)
  • Enums with associated values become sealed classes, not enum class — the false friend that costs everyone an afternoon
  • No ARC: cycles are not a leak, weak/unowned have no purpose, and there is no deinit and no defer — cleanup is use { }
  • throws vanishes from the signature (all exceptions are unchecked), so nothing tells a caller a function can fail
  • Null safety is as strong as Swift's until the Java boundary, where platform types switch the checking off — that is where NPEs in "null-safe" Kotlin come from
  • No actors and no Sendable: a Mutex you take by hand does that job, and forgetting it still compiles
Rust Pre-Alpha

The shortest trip into systems programming there is. These two languages rhyme constantly — Optional is Option, enums with associated values are enums with data, protocols are traits, guard let is let … else, switch is match, and both are memory-safe without a garbage collector. What changes is when the safety is established: ARC does it at run time, and ownership does it at compile time.

  • let second = first is the most different line on the page: Swift copies (structs, copy-on-write), Rust moves — the original name becomes unusable, and .clone() is the visible, explicit opt-out
  • The borrow checker permits many readers or one writer, never both — so iterator invalidation, use-after-free, and data races are all the same compile error
  • Lifetimes are the genuinely new idea: ARC kept a referent alive at run time, and Rust instead proves statically that your reference cannot outlive it
  • Reference counting is opt-in (Rc/Arc), not the default every class pays for — most values have one owner and cost nothing
  • throws/try becomes Result<T, E> and the ? operator: a failure is a value in the return type, so it can be stored, mapped, and collected
  • A Mutex owns its data, so "forgot to take the lock" is unwritable — and async ships with no runtime at all, which is the biggest surprise coming from Swift
C# Pre-Alpha

The closest match in shape that Swift has. The only mainstream target that keeps the struct/class split — real value types and reference types — plus properties, reified generics, extension methods, pattern matching, and the async/await syntax C# invented and Swift later adopted. Which is exactly why the places it deceives you are worth knowing.

  • string? is not String?: nullable reference types are a compiler analysis that produces warnings and is erased at run time — a NullReferenceException is still reachable
  • var means inferred, not mutable — it is both of Swift's keywords and carries neither meaning; there is no general-purpose let
  • Structs are value types with no copy-on-write, and indexing a collection of them hands you a copy — mutate it and nothing happens
  • Records give you value equality and with expressions (ada with { Age = 37 }) — the one thing here that is nicer than Swift
  • No enums with associated values: a C# enum is a named integer (and (Status)99 is legal), so sum types become sealed record hierarchies
  • Pattern matching has overtaken Swift's: property patterns, relational patterns, and list patterns all exist here and do not exist there
Scala Pre-Alpha

The other language that blends OOP and functional programming under a serious type system. The rhyme is real — Option, sealed hierarchies, traits, match, immutability by default — so this page is about where Scala goes further: implicits, higher-kinded types, and one for-comprehension that chains every fallible thing you have.

  • Implicits have no Swift counterpart: one mechanism gives you context parameters, extension methods, and type classes the compiler assembles per call site
  • Higher-kinded types (F[_]) abstract over List/Option/Future themselves — Swift cannot express the signature at all
  • for … yield is sugar for flatMap, so the same three lines work over Option, Either, Try, List and Future
  • Pattern matching is extensible: any object with an unapply is a pattern, so you can match on a parsed integer instead of parsing before the switch
  • Traits hold state and stack by linearization — a protocol extension that can also be a composable decorator
  • The costs: exhaustiveness is a warning (turn on -Xfatal-warnings), a case class is a reference type, and Future is eager with no structured concurrency
Ruby ⚡ Works Offline ⚡ Offline

Every guarantee traded for expressiveness. No compiler, no types, no optionals, no exhaustiveness — and in return, a language where classes are never closed, blocks are everywhere, and methods can be written while the program runs. Swift protects you from yourself; Ruby hands you the keys.

  • Duck typing replaces protocols: anything with the right method works, no conformance declared — and a misspelled method is a NoMethodError when that line runs
  • nil is an object with methods (nil.to_a is []), &. is ?., and only nil and false are falsy — so 0 and "" are true
  • Blocks are the whole language: File.open(path) { } is Swift's defer expressed as an ordinary method, and Enumerable is the richest collection library on this site
  • Open classes: reopen String or redefine Integer#even? globally — extension methods with none of the guardrails (Rails is built on it)
  • Mixins replace protocols and protocol extensions: define <=>, include Comparable, inherit every operator free
  • Metaprogramming is idiomatic, not a hack: define_method and method_missing are how attr_accessor itself works
Drag cards to reorder · your order is saved locally