How Languages Run Your Code

From source to execution — explore how different languages handle types, compilation, and runtime. Interactive playgrounds let you see the differences firsthand.

The Execution Spectrum

Every programming language transforms your source code into something the computer can execute — but they do it very differently. Understanding this spectrum helps you choose the right tool for each job.

🐍

Interpreted

1.Source code → Bytecode
2.VM reads bytecode
3.Executes line by line

Bytecode is a compact, intermediate representation. It's not human-readable like source code, but not machine code either. A Virtual Machine (VM) interprets it at runtime.

📘

Transpiled

1.TypeScript → JavaScript
2.Types are erased
3.Browser runs plain JS

Transpilation converts one language to another at the same level. TypeScript adds types for developer tooling, then strips them away to produce standard JavaScript.

🦀🐹

Compiled

1.Source → Machine code
2.No interpreter needed
3.CPU runs directly

Machine code is the native language of your CPU. The compiler does all the work upfront, producing a binary that runs at maximum speed without any intermediary.

🐍

Dynamic Typing: Python

Python is dynamically typed — variables can hold any type, and type errors are only discovered when code actually runs.

Key Insight

Python type hints (like : str) are documentation only. The interpreter ignores them completely. You can pass a number where a string is expected and Python won't complain until something actually breaks at runtime.

Variables and Types

Python variable types and operations. Notice how type() shows the type is determined at runtime.

Variables and Types
# Python variables and types
name = "Alice"
score = 100
height = 1.75
is_active = True

print(f"Name: {name} (type: {type(name).__name__})")
print(f"Score: {score} (type: {type(score).__name__})")
print(f"Height: {height} (type: {type(height).__name__})")
print(f"Active: {is_active} (type: {type(is_active).__name__})")
Output:
Name: Alice (type: str)
Score: 100 (type: int)
Height: 1.75 (type: float)
Active: True (type: bool)
Interactive playground available on desktop

Python Bytecode

Use the dis module to see what Python compiles your code to. This bytecode runs on the Python VM.

Python Bytecode
import dis

def greet(name):
    return f"Hello, {name}!"

# Show the bytecode
print("Function bytecode:")
dis.dis(greet)

# Call the function
print("\nOutput:")
print(greet("World"))
Output:
Function bytecode:
  4           0 RESUME                   0

  5           2 LOAD_CONST               1 ('Hello, ')
              4 LOAD_FAST                0 (name)
              6 FORMAT_VALUE             0
              8 LOAD_CONST               2 ('!')
             10 BUILD_STRING             3
             12 RETURN_VALUE

Output:
Hello, World!
Interactive playground available on desktop

📖 Reading Python Bytecode

When you see bytecode output like this, here's what each part means:

  3           0 RESUME                   0
              ↑           ↑                ↑
           line#     offset           argument

  4           2 LOAD_CONST               1 ('Hello, ')
              4 LOAD_FAST                0 (name)
              6 FORMAT_VALUE             0
              8 BUILD_STRING             3
             10 RETURN_VALUE
LOAD_CONST — Push a constant value onto the stack
LOAD_FAST — Load a local variable by index
FORMAT_VALUE — Format for f-string interpolation
BUILD_STRING — Concatenate N items into a string
RETURN_VALUE — Return the top of the stack
BINARY_OP — Perform arithmetic (+, -, *, /)

The Python VM is a stack machine — operations push and pop values from a stack. This is simpler than register-based machines but requires more instructions.

📘

Types That Disappear: TypeScript

TypeScript adds static types to JavaScript — but they exist only for developer tooling. At runtime, it's just JavaScript.

Key Insight

TypeScript types are completely erased during transpilation. The browser never sees : string or : number. This means TypeScript provides compile-time safety while keeping full JavaScript compatibility.

Type Erasure in Action

Click "Transpile" to see how TypeScript types are removed. Notice how generics, interfaces, and type annotations all vanish.

TypeScript → JavaScript
TypeScript
// Interfaces are completely erased
interface User {
  id: number;
  name: string;
  email: string;
}

// Type annotations disappear
function greet(user: User): string {
  return `Hello, ${user.name}!`;
}

// Generics become plain functions
function identity<T>(value: T): T {
  return value;
}

// Type assertions vanish
const count = "42" as unknown as number;

// Usage - types only exist at compile time
const user: User = { id: 1, name: "Alice", email: "[email protected]" };
console.log(greet(user));
console.log(identity<string>("TypeScript"));
Types Erased
JavaScript
// Interfaces are completely erased
// Type annotations disappear
function greet(user) {
    return `Hello, ${user.name}!`;
}
// Generics become plain functions
function identity(value) {
    return value;
}
// Type assertions vanish
const count = "42";
// Usage - types only exist at compile time
const user = { id: 1, name: "Alice", email: "[email protected]" };
console.log(greet(user));
console.log(identity("TypeScript"));
Interactive playground available on desktop
🦀🐹

Static Compiled: Rust & Go

Compiled languages check types at compile time and produce machine code directly. No interpreter, no VM, no runtime type information.

Key Insight

In Rust and Go, types are checked before the binary exists. If there's a type error, you can't even create the executable. The compiler rejects invalid code entirely, making certain bugs impossible.

🦀Rust
// Types are part of the compiled binary
fn main() {
    // Type inference: Rust figures out types
    let x = 42;        // i32
    let y = 3.14;      // f64
    let name = "Alice"; // &str

    // Explicit types when needed
    let count: u64 = 1_000_000;

    // Type errors = compilation fails
    // let bad: i32 = "hello"; // ❌ Won't compile!

    // The Result type forces error handling
    let parsed: Result<i32, _> = "42".parse();
    match parsed {
        Ok(n) => println!("Parsed: {}", n),
        Err(e) => println!("Error: {}", e),
    }
}
Compiles to native machine code via LLVM
🐹Go
package main

import "fmt"

func main() {
    // Type inference with :=
    x := 42          // int
    y := 3.14        // float64
    name := "Alice"  // string

    // Explicit types
    var count uint64 = 1_000_000

    // Type errors = compilation fails
    // var bad int = "hello" // ❌ Won't compile!

    // Multiple return values for errors
    if parsed, err := strconv.Atoi("42"); err != nil {
        fmt.Println("Error:", err)
    } else {
        fmt.Println("Parsed:", parsed)
    }

    fmt.Println(x, y, name, count)
}
Compiles to native machine code (fast compilation)

Side-by-Side Comparison

See the same concepts implemented across different languages. Interactive playgrounds for Python and TypeScript — static examples for Rust and Go.

Type Annotations

How each language handles type information

🐍Python
python
def greet(name: str) -> str:
    """Types are hints only - not enforced!"""
    return f"Hello, {name}!"

# Python runs this without complaint
print(greet("World"))
print(greet(42))  # Works! Types are just hints
Output:
Hello, World!
Hello, 42!
Interactive playground available on desktop
💡 Types checked at RUNTIME (or not at all!)
📘TypeScript
TypeScript
TypeScript
function greet(name: string): string {
  return `Hello, ${name}!`;
}

// greet(42);  // ❌ Compile error!
// This line won't even compile

console.log(greet("World"));
Types Erased
JavaScript
function greet(name) {
    return `Hello, ${name}!`;
}
// greet(42);  // ❌ Compile error!
// This line won't even compile
console.log(greet("World"));
Interactive playground available on desktop
💡 Types checked at BUILD time, then erased
🦀Rust
Rust
Read only
fn greet(name: &str) -> String {
    format!("Hello, {}!", name)
}

fn main() {
    // greet(42);  // ❌ Won't even compile!
    // Types are part of the program

    println!("{}", greet("World"));
}
Types checked at compile time, baked into the binary
💡 Types checked at COMPILE time, enforced forever

Variables & Types

How variables and types work across paradigms

🐍Python
python
# Dynamic typing - variable type can change
x = 42          # int
print(f"x = {x}, type: {type(x).__name__}")

x = "hello"     # now it's a string!
print(f"x = {x}, type: {type(x).__name__}")

x = [1, 2, 3]   # now it's a list!
print(f"x = {x}, type: {type(x).__name__}")
Output:
x = 42, type: int
x = hello, type: str
x = [1, 2, 3], type: list
Interactive playground available on desktop
💡 Variable type can change at runtime
📘TypeScript
TypeScript
TypeScript
// Static typing - type is fixed at declaration
let x: number = 42;
console.log(`x = ${x}, type: number`);

// x = "hello";  // ❌ Type error!
// TypeScript won't let you reassign to different type

// But 'any' escapes the type system:
let y: any = 42;
y = "hello";  // This works with 'any'
console.log(`y = ${y}, type: any (dangerous!)`);
Types Erased
JavaScript
// Static typing - type is fixed at declaration
let x = 42;
console.log(`x = ${x}, type: number`);
// x = "hello";  // ❌ Type error!
// TypeScript won't let you reassign to different type
// But 'any' escapes the type system:
let y = 42;
y = "hello"; // This works with 'any'
console.log(`y = ${y}, type: any (dangerous!)`);
Interactive playground available on desktop
💡 Types fixed at declaration (but JS underneath is dynamic)
🐹Go
Go
Read only
package main

import "fmt"

func main() {
    // Static typing - type is permanent
    var x int = 42
    fmt.Printf("x = %d, type: int\n", x)

    // x = "hello"  // ❌ Won't compile!

    // Type inference still picks one type
    y := 42  // Go infers: int
    fmt.Printf("y = %d, type: int (inferred)\n", y)

    // y = "hello"  // ❌ Still won't compile!
}
Strong static typing with type inference
💡 Type determined at compile time, never changes

From Source to Execution

What happens when you run code in each language

🐍Python
python
import dis

def add(a, b):
    return a + b

# Show the bytecode Python generates
print("Python compiles to bytecode:")
dis.dis(add)

print("\nThen CPython VM executes it:")
print(f"add(2, 3) = {add(2, 3)}")
Output:
Python compiles to bytecode:
  4           0 RESUME                   0

  5           2 LOAD_FAST                0 (a)
              4 LOAD_FAST                1 (b)
              6 BINARY_OP                0 (+)
             10 RETURN_VALUE

Then CPython VM executes it:
add(2, 3) = 5
Interactive playground available on desktop
💡 Source → Bytecode → Python VM interprets
📘TypeScript
TypeScript
TypeScript
// TypeScript with types
function add(a: number, b: number): number {
  return a + b;
}

// Generic function with type constraint
function identity<T>(value: T): T {
  return value;
}

const result = add(2, 3);
const str = identity("hello");
console.log(result, str);
Types Erased
JavaScript
// TypeScript with types
function add(a, b) {
    return a + b;
}
// Generic function with type constraint
function identity(value) {
    return value;
}
const result = add(2, 3);
const str = identity("hello");
console.log(result, str);
Interactive playground available on desktop
💡 Source → Type-check → Erase types → JavaScript
🦀Rust
Rust
Read only
fn add(a: i32, b: i32) -> i32 {
    a + b  // No semicolon = return value
}

// Generic function (monomorphized at compile)
fn identity<T>(value: T) -> T {
    value
}

fn main() {
    let result = add(2, 3);
    let s = identity("hello");
    println!("{} {}", result, s);
    // Types are gone, machine code remains
}
Compiles directly to optimized machine code
💡 Source → LLVM IR → Machine code (no runtime)

Summary: Type Systems Compared

Aspect🐍 Python📘 TypeScript🦀 Rust / 🐹 Go
When types checkedRuntime (or never)Build timeCompile time
What runsBytecode on VMJavaScriptMachine code
Types at runtimeYes (reflection)No (erased)No (baked in)
Type errors caughtLate (when code runs)Early (before deploy)Earliest (no binary)
Variable typesCan changeFixed (but JS dynamic)Permanently fixed
Best forScripts, data science, prototypesWeb apps, large JS codebasesSystems, performance-critical

How This Page Works

🐍 Python Playground

  • Powered by Pyodide — CPython compiled to WebAssembly
  • ~15MB download on first run
  • Full Python 3.11 with standard library
  • Runs entirely in your browser sandbox

📘 TypeScript Playground

  • Uses the official TypeScript compiler
  • Transpiles in-browser via ts.transpileModule()
  • Shows type erasure in real-time
  • No server required — all client-side

Mobile users see pre-computed output since WebAssembly can be resource-intensive on mobile devices.

Explore more interactive learning experiences: