
## Vary vs Python

A side-by-side comparison of syntax and semantics. Python is a far more mature language with a larger ecosystem. Where Vary has something built in, Python typically has third-party equivalents.

For a property-testing-specific comparison, see [Hypothesis comparison](/docs/hypothesis-comparison/).

| **Feature** | **Python** | **Vary** |
|---------|--------|------|
| Typing | Dynamic, optional hints | Static, enforced at compile time |
| Execution | Interpreted | Compiled to JVM bytecode |
| Variables | `x = 42` (always mutable) | `let x = 42` / `mut x = 0` |
| Type annotations | Optional | Parameter types required |
| Null handling | Anything can be `None` | Only `T?` types can be `None` |
| Strings | `str` | `Str` |
| Booleans | `True` / `False` | `True` / `False` (same) |
| Collections | `list`, `dict`, `set` | `List[T]`, `Dict[K, V]`, `Set[T]` |
| Data types | `@dataclass` | `data` keyword |
| Inheritance | Multiple inheritance | No inheritance; `interface` + `implements` |
| Enums | `enum.Enum` | `enum` with payloads and exhaustive match |
| Pattern matching | `match`/`case` (3.10+) | `match`/`case` with guards and or-patterns |
| Concurrency | `async`/`await`, threads | `spawn`/`join` |
| Lambdas | `lambda x: x + 1` | `lambda x: Int: x + 1` (typed) |
| Error handling | `try`/`except`/`raise` | Same syntax |
| Contracts | No built-in (third-party: `icontract`, `deal`) | Built-in `in {}` / `out (r) {}` / `old()` |
| Imports | `from x import y` | Same, plus aliases |
| Web/HTTP | Flask, Django | `expose Interface via http` |
| Testing | pytest, unittest | Built-in `test` DSL + `observe` |
| Mutation testing | Third-party (mutmut) | Built-in `vary mutate` |
| Runtime | CPython / PyPy | JVM (Eclipse Temurin 25) |

## Runtime differences

Python runs on CPython (or PyPy). Vary compiles to JVM bytecode and runs on the Java Virtual Machine. This has practical consequences:

| **Area** | **Python** | **Vary** |
|---------|--------|------|
| Startup time | Fast (tens of ms) | Slower (hundreds of ms due to JVM cold start) |
| Long-running performance | Good, but limited by GIL for CPU-bound threads | JIT-compiled over time; real multithreading |
| Memory model | Reference counting + cycle collector | Garbage collected (G1, ZGC, etc.) |
| Integer size | Arbitrary precision | 64-bit (`long`) |
| Float size | 64-bit (`double`) | 64-bit (`double`) |
| Collections | Native Python objects | Java `ArrayList`, `HashMap`, `HashSet` under the hood |
| Package ecosystem | pip, PyPI (500k+ packages) | No package manager yet; stdlib only |

Python starts faster. The JVM is a better fit for long-running services where JIT warmup pays off and real threading matters.

## Python features intentionally excluded

Each conflicts with static analysis, predictable compilation, or explicit control flow.

| **Feature** | **Why excluded** |
|---------|----------------------|
| Metaclasses | Makes class definition non-deterministic; breaks static analysis |
| General-purpose decorators | Hides control flow; conflicts with explicit execution |
| `*args` / `**kwargs` | Undermines explicit signatures and static typing |
| Context managers (`with`) | Requires implicit protocol hooks |
| Generators (`yield`) | Hidden state machines; complicates bytecode generation |
| C extensions | Incompatible with JVM bytecode |
| Dynamic import hooks | Breaks compile-time dependency analysis |
| Monkey patching | Violates immutability guarantees |
| Walrus operator (`:=`) | Hides mutation inside expressions |
| Wildcard imports (`from x import *`) | Makes static analysis non-local |
| Bare `except:` | Swallows unexpected failures |
