Menu
Learn
TutorialSnippetsvs Other Languages
Build
EditorPlayground
Docs
Language SpecStatus
Platform
ServerAbout Transpilation
Safebox
Why UAnalysisPossibilities

U vs other languages

Pick a language. See what changes. Every comparison covers syntax, safety, capabilities, and what an LLM can understand about the code.

Type declarations

C reads inside-out. U reads left to right.

C
void (*(*compose(
    void (*(*f)(int))(char),
    void (*(*g)(char))(float)
))(float))(int);
U
f compose(
    f: (I) -> ((N8) -> ()),
    g: (N8) -> ((R) -> ())
) -> (R) -> ((I) -> ())
C
// array of 10 pointers to functions
// returning pointer to array of 5 ints
int (*(*handlers[10])(const char *))[5];
U
handlers: [(S) -> [I; 5]; 10]
C
// the classic signal() declaration
void (*signal(int sig,
    void (*handler)(int)))(int);
U
f signal(sig: I, handler: (I) -> ())
    -> (I) -> ()

Memory safety

C trusts the programmer. U trusts the compiler.

C
char *buf = malloc(10);
strcpy(buf, user_input);  // buffer overflow?
free(buf);
printf("%s", buf);        // use-after-free
// compiles fine, crashes at runtime
U
buf = user_input.slice(1, 10)
// S is length-tracked, UTF-8
// no malloc, no free, no overflow
// reference counting handles lifetime
// use-after-free is structurally impossible

Null-terminated strings

Every buffer overflow in C starts with a missing null terminator or a miscounted length. U strings are length-tracked, and the byte literal type N8 (1–255) makes null bytes unrepresentable.

C
char name[5] = "hello"; // no room for \0
strlen(name);            // undefined behavior
strcat(name, "!");       // heap corruption
U
name = "hello"           // S, length-tracked
name.length              // 5 (characters, not bytes)
name + "!"               // "hello!" — safe concat
bytes: [N8] = 'hello'   // no \0 possible (N8 ≥ 1)

Capability tracking

C has no concept of what a function does to the outside world. U tracks it in the type system.

C
// what does this function do?
// read the implementation to find out
int process(Request *req);

// could do anything: files, network,
// database, exec, crypto — no way to
// know without reading every line
U
// the signature tells you everything:
f process(req: Request) -> I +DB +Net

// +DB: touches the database
// +Net: makes network calls
// no +IO: doesn't touch filesystem
// no +Exec: doesn't spawn processes
// compiler-verified, not a comment

Invisible side effects

In C, any function might do I/O, write to a database, send an email, or launch a process. You can't tell from the signature. You discover what a function does by reading it — or by running it and watching.

C
int process(Request *req);
// What does this do? Read the
// implementation. Follow every call.
// Hope you don't miss the system()
// buried in a helper three levels deep.
U
f process(req: Request) -> I +DB +Net
// The +DB and +Net are compiler-verified.
// If the function calls system(), it needs
// +Exec — and the compiler rejects it
// without that modifier.

U's default is -E: functions are pure. Side effects require +E. One modifier, not a monadic wrapper. Effects propagate upward through call chains — every signature tells you the truth.

Data races

Two threads write to the same memory without synchronization. Undefined behavior in C. The cause of Therac-25 (radiation overdose), the Mars Pathfinder reboot loop, and countless production outages. U makes races a compile error.

C
// Thread 1:
account->balance -= amount;
// Thread 2:
account->balance += deposit;
// Data race — undefined behavior.
// Might corrupt memory, might "work,"
// might produce wrong results silently.
U
// MVCC — new version, no in-place mutation:
<< (
    sender << {balance: sender.balance - amount}
    recipient << {balance: recipient.balance + amount}
)  // atomic: both succeed or neither does
// No locks, no CAS, no deadlocks.

Undefined behavior — the silent contract

C has over 200 categories of undefined behavior. Signed integer overflow, null dereference, out-of-bounds access, use-after-free, uninitialized reads, strict aliasing violations — all compile without warnings, all produce "correct" results in testing, all explode in production. The compiler is allowed to do anything, including optimizing away your safety checks.

C
// The compiler can REMOVE this null check:
int deref(int *p) {
    if (p == NULL) return -1;
    return *p;
    // GCC -O2: "p is dereferenced, so p
    // can't be NULL, so the check is dead
    // code." Deletes your safety check.
U
f deref(p: I+N) -> I
    p == none ? r => -1
    r => p
// +N is a compiler-checked contract.
// The check cannot be optimized away
// because none IS a valid state of I+N.

Signed integer overflow in C is UB. INT_MAX + 1 can do anything. U's integers are defined on overflow — wrapping for fixed-width, arbitrary precision for I. No UB category exists in U.

Format string attacks

C's printf(user_input) is a remote code execution vulnerability. The format string reads the stack. %n writes to memory. This is because C conflates the template and the data in one function call.

C
printf(username);  // if username contains
                   // %x%x%x%n — stack leak
                   // + arbitrary write
U
System.out(username)  // S, not a format
// Templates are a separate type:
System.out(`Hello, {{username}}`)
// username.to_string() — no format codes

SIMD — platform intrinsics vs one modifier

Vectorizing a loop in C means _mm256_mullo_epi32 (x86) or vmulq_s32 (ARM). Non-portable, unreadable, expert-only. Auto-vectorization is unreliable — slight code changes break it silently.

C (x86 SIMD)
__m256i a = _mm256_loadu_si256(src);
__m256i b = _mm256_set1_epi32(3);
__m256i r = _mm256_mullo_epi32(a, b);
_mm256_storeu_si256(dst, r);
// x86 only. ARM needs different code.
U
data: [I +V] = load(src)
result = data.map(val => val * 3)
// +V: compiler emits SSE/AVX on x86,
//     NEON on ARM, WASM SIMD in browser.
// Same source. Same types.

Memory management — no GC, no manual malloc

C makes you manage memory manually. GC languages (Java, Go, Python) take it away but introduce pauses. U's approach: stack by default, reference counting for heap objects, compile-time cycle prevention, and arena allocators for request-based workloads — like PHP's per-request memory, but without restarting the process.

C
// Manual — your problem:
char *buf = malloc(4096);
// ... 200 lines of code ...
free(buf); // forgot? memory leak
           // double free? corruption
           // use after? crash
U
// Stack by default — no allocation:
buf = process(input)

// Heap when needed — refcounted:
data = load_config() +R  // heap, ARC

// Webserver arena — reset per request:
// entire request's memory freed in one
// operation. No GC pause. No leak.
// Long-running process, zero accumulation.

U's webserver uses arena allocation per request — like PHP's per-request memory model, but the process stays alive across requests. No GC pauses. No memory accumulation. The arena resets in microseconds. Long-running servers run for months without memory growth.

LLMs can't audit C safely

An LLM reading C must follow every pointer, track every allocation, understand every macro expansion, and detect every undefined behavior — across every file in the project. It misses things. Buffer overflows hide in helper functions three levels deep. Use-after-free hides behind opaque typedefs. U's transpiled form makes every capability and every type visible in the function signature. The LLM reads signatures, not implementations.

FeatureCU
Memory safetymanual malloc/freereference counting, no dangling pointers
Null safetyNULL everywhere+N explicit, compiler-checked
Buffer overflowstrcpy, strcat, getslength-tracked S, bounds-checked [T]
Capability trackingnone+IO, +Net, +DB, +Crypto, +Exec, +Unsafe
String encodingchar* (no encoding)S is UTF-8, always
LLM can audit capabilitiesmust read entire codebaseread the function signatures
Compiles to CU → C → GCC → native binary

Smart pointer hell

C++ solved C's memory problem by adding five pointer types, three of which interact badly with templates. U solved it with one modifier.

C++
std::shared_ptr<std::vector<
  std::unique_ptr<std::unordered_map<
    std::string,
    std::variant<int,
      std::shared_ptr<
        std::vector<double>>>>>>> data;
U
data: [{S: I | [R] +R}] +R

Template metaprogramming

C++ SFINAE is a Turing-complete compile-time language that nobody asked for. U has type constraints with one keyword.

C++
template<typename T,
  typename = std::enable_if_t<
    std::is_base_of_v<Serializable, T> &&
    std::is_move_constructible_v<T> &&
    !std::is_same_v<std::decay_t<T>,
      std::string>>>
std::optional<std::vector<
  std::pair<std::string, T>>>
deserialize_all(std::istream& input,
  std::function<bool(const T&)> filter);
U
f deserialize_all(
    input: Stream,
    filter: (T) -> L
) -> [{S, T}]+N
    x T: Serializable

The Boost.Asio monstrosity

C++
boost::asio::awaitable<
  std::expected<
    std::vector<std::pair<
      std::string,
      boost::json::value>>,
    boost::system::error_code>>
async_fetch_all(
  boost::asio::io_context::strand& strand,
  std::span<const boost::urls::url> urls,
  boost::asio::ssl::context& ssl_ctx,
  std::chrono::milliseconds timeout);
U
a f fetch_all(
    urls: [URL],
    timeout: I
) -> [{S, JSON.Value}] +Net

CRTP and virtual dispatch

C++
template<typename Derived>
class Base {
    template<typename... Args>
    auto invoke(Args&&... args)
      -> decltype(
        static_cast<Derived*>(this)
          ->impl(std::forward<Args>(
              args)...))
    {
      return static_cast<Derived*>
        (this)->impl(
          std::forward<Args>(args)...);
    }
};
U
d Base
    f invoke(...args) -> T
        r => t.impl(...args)

Async function coloring

In C++20, if a function is a coroutine, every caller must handle the coroutine return type. Your codebase splits into async and sync halves that can't freely interoperate. Refactoring a deep function to async forces changes up the entire call chain.

C++
// Must choose: sync or async?
std::string process(Data d);
// vs
task process(Data d);
// Changing one forces changes everywhere
// that calls it.
U
// One function. a marks the CALL site:
f process(data: S) -> Result
    r => parse(data)

result = process(local_data)     // sync
result = a process(remote_data)  // async
// Same function, both uses.

67 keywords and counting

C++ has 97 reserved words. Every new standard adds more: co_await, co_yield, co_return, concept, requires, consteval, constinit. U has 13 single-letter keywords that compose: a f = async function, z f = compile-time, u f = AI-managed. The modifier system (+M, -E, +R) handles what C++ adds keywords for.

GPU programming is a separate language

CUDA is C++ but not C++. Different compiler, different rules, different memory model. You maintain two codebases. Metal, OpenCL, WGSL — same story. Every GPU target is a separate language with separate tooling.

C++ / CUDA
// Host code (C++):
float *d_data;
cudaMalloc(&d_data, n * sizeof(float));
cudaMemcpy(d_data, h_data, n*sizeof(float),
    cudaMemcpyHostToDevice);
// Device code (CUDA — separate file):
__global__ void scale(float *d, int n) {
    int i = blockIdx.x * blockDim.x
          + threadIdx.x;
    if (i < n) d[i] *= 3.0f;
}
scale<<>>(d_data, n);
cudaMemcpy(h_data, d_data, ...);
U
data: [R] +R(GPU) = load(input)
result = data.map(val => val * 3.0)
// +R(GPU): data lives on GPU.
// .map() compiles to WGSL compute shader.
// Compiler checks handler is pure (+V).
// Same source, same types, GPU target.
// No cudaMalloc. No memcpy. No kernel.

Copy, move, or reference? Five choices

C++ has copy constructors, move constructors, copy assignment, move assignment, and references (lvalue and rvalue). The Rule of Five says: if you define one, you must define all five. Get one wrong and you have a silent bug. U has one: c copies, everything else is a borrow.

C++
class Widget {
    Widget(const Widget&);             // copy ctor
    Widget(Widget&&);                  // move ctor
    Widget& operator=(const Widget&);  // copy assign
    Widget& operator=(Widget&&);       // move assign
    ~Widget();                         // destructor
    // Forget one? Silent double-free
    // or leaked resource.
U
d Widget
    data: [I] +R

original = Widget({data: [1, 2, 3]})
copy = c original       // explicit copy
// No move semantics. No Rule of Five.
// Refcounting handles lifetime.
// Default is borrow (-M, read-only).

Exceptions — invisible control flow

Any C++ function can throw. The signature doesn't say what. noexcept is opt-in and easy to forget. Exception safety (basic, strong, nothrow) is a convention, not a compiler check. catch(...) {} — silent swallowing — compiles without warning.

C++
Widget load(string path); // throws what?
// std::runtime_error? std::bad_alloc?
// A custom exception? All three?
// Read the implementation to find out.
U
f load(path: S) -> Widget ! IOError +IO
// ! IOError: this is what can go wrong
// +IO: this touches the filesystem
// Both compiler-checked. No silent throw.

LLMs drown in C++ complexity

C++ has the largest grammar of any mainstream language. Template instantiation, SFINAE, ADL (argument-dependent lookup), implicit conversions, operator overloading, multiple inheritance with virtual bases — an LLM reading C++ is guessing at overload resolution, not understanding it. U's 13 keywords and modifier system mean the LLM understands the entire language from a 1,600-token primer. The same code that's readable to humans is readable to machines.

FeatureC++U
Smart pointersshared_ptr, unique_ptr, weak_ptr+R (heap), default is stack — one annotation
TemplatesTuring-complete, error novelstype constraints with x T: Bound
Asynccoroutines (C++20), complexa keyword, compiler-managed fibers
Error handlingexceptions + error codes + optional+N for nullable, structured errors
Build systemCMake, Meson, Bazel, vcpkg...single compiler, no build config
Capability trackingnonesix categories, compiler-verified

Lifetime annotations

Rust gives you memory safety at the cost of lifetime annotations that infect every function signature. U uses reference counting — no lifetimes, no borrow checker, no fighting the compiler.

Rust
fn longest<'a>(
    x: &'a str,
    y: &'a str
) -> &'a str {
    if x.len() > y.len() { x } else { y }
}

// try returning a reference to a local:
fn broken<'a>() -> &'a str {
    let s = String::from("hello");
    &s  // ERROR: borrowed value
        // does not live long enough
}
U
f longest(x: S, y: S) -> S
    r => x.length > y.length ? x ! y

// returning a local is fine:
f not_broken() -> S
    r => "hello"  // ref-counted, no lifetime

Error handling

Rust
fn read_config(path: &str)
    -> Result<Config, Box<dyn Error>>
{
    let content = fs::read_to_string(path)?;
    let config: Config =
        serde_json::from_str(&content)?;
    Ok(config)
}
U
f read_config(path: S) -> Config +IO
    content = File.read(path)
    r => JSON.parse(content)

U's +IO tells you the function touches the filesystem. Rust's signature says nothing about capabilities — you need to read the body.

No garbage collector — but different tradeoffs

Rust achieves memory safety with zero runtime cost through the borrow checker. U achieves it with reference counting (small runtime cost) plus compile-time cycle prevention. Rust rejects valid programs the borrow checker can't prove safe. U never rejects valid programs — the cost is a few nanoseconds per reference count operation.

Rust
// Borrow checker rejects this:
let mut v = vec![1, 2, 3];
let first = &v[0];
v.push(4);  // ERROR: cannot borrow
            // `v` as mutable because
            // it's borrowed as immutable
println!("{}", first);
U
// U handles this naturally:
v = [1, 2, 3] +M +R
first = v[1]          // copy (I is stack)
v.append(4)           // fine
System.out(S(first))  // 1 — no conflict

Async coloring — Rust has it too

Rust's async fn returns a Future. Every caller must .await it. The async/sync split infects the entire codebase. U marks the call site, not the function definition.

Invisible side effects — Rust has them too

Rust's type system tracks ownership and lifetimes but not what a function does to the outside world. A Rust function that reads a file, queries a database, and sends an HTTP request looks the same in the signature as one that adds two numbers. U's capability modifiers make effects visible.

Rust
fn process(req: &Request) -> Response {
    // reads config from disk (+IO)
    // queries postgres (+DB)
    // calls external API (+Net)
    // the signature reveals: nothing.
}
U
f process(req: Request) -> Response
    +IO +DB +Net
// Every capability declared. Compiler
// verifies. Remove +Net and any HTTP
// call inside becomes a compile error.

Where Rust wins: zero-cost abstractions with zero runtime overhead, no garbage collector, and a mature ecosystem. U uses reference counting (small runtime cost) and is a younger language. Where U wins: readability, capability tracking, cross-language analysis via transpilation, and the ability to analyze existing PHP/JS/Python codebases without rewriting them.

FeatureRustU
Memory safetyborrow checker (zero-cost)ref counting (small runtime cost)
Lifetimes'a annotations everywherenot needed — ref counting
Learning curvesteep (borrow checker)reads like pseudocode
Capability trackingnone in the type system+IO, +Net, +DB, etc.
Existing code analysisRust onlytranspile PHP/JS/Python → U
Unsafe escape hatchunsafe {} blocks+Unsafe modifier, tracked

Error handling

Go's if err != nil on every other line. U's +N makes the compiler enforce it.

Go
func loadUser(id int) (*User, error) {
    row, err := db.Query("SELECT...", id)
    if err != nil {
        return nil, err
    }
    user, err := parseRow(row)
    if err != nil {
        return nil, err
    }
    avatar, err := fetchAvatar(user.AvatarURL)
    if err != nil {
        return nil, err // forgot to close row?
    }
    return user, nil
}
U
f load_user(id: I) -> User +DB +Net
    row = DB.query(
        SQL`SELECT * WHERE id={{id}}`
    )
    user = parse_row(row)
    user.avatar = fetch_avatar(user.avatar_url)
    r => user

U's signature declares +DB and +Net. Go's signature says error — it doesn't tell you whether the error comes from the database, the network, or a parse failure.

Generics

Go
func Map[T any, U any](
    s []T, f func(T) U,
) []U {
    result := make([]U, len(s))
    for i, v := range s {
        result[i] = f(v)
    }
    return result
}
U
// built-in — no need to define:
result = items.map(item => transform(item))

Nil panics at runtime

Go has nil. It panics at runtime when you dereference it. There's no compile-time check. Go's designers chose simplicity over safety — and every Go program pays for it with defensive nil checks or production panics.

Go
func getUser(id int) *User {
    return nil // legal return value
}

user := getUser(42)
fmt.Println(user.Name) // PANIC at runtime
U
f get_user(id: I) -> User +DB
    // must return User, not none

f get_user(id: I) -> User+N +DB
    // +N: caller MUST handle none
    // compiler rejects .name without check

No generics for a decade — and limited now

Go shipped without generics in 2009. Added them in 2022 (Go 1.18) with constraints that are still more limited than what Rust, Haskell, or U offer. The result: years of code using interface{} and type assertions — runtime type checks masquerading as type safety.

GC pauses — the latency tax

Go's garbage collector is good — 1-3ms pauses. But for latency-sensitive systems (trading, real-time audio, game servers), any pause is too much. U has no garbage collector. Reference counting is deterministic — O(1) per operation, no pauses, no stop-the-world. Arena allocation for request-based workloads gives you PHP-style per-request cleanup without restarting the process.

Go
// GC runs periodically:
// - 1-3ms pause (typical)
// - 10ms+ under memory pressure
// - GOGC tuning is a dark art
// - runtime.GC() forces collection
// Your p99 latency includes GC pauses.
U
// No GC. Deterministic cleanup:
// Stack values: freed on scope exit
// +R heap values: refcount → 0 → freed
// Webserver arena: reset per request
//   (microseconds, not milliseconds)
// p99 = your code's actual latency.
FeatureGoU
Error handlingif err != nil (verbose)+N nullable, structured
Genericsadded in 1.18, basicfull parametric types
Capability trackingnonesix categories
Concurrencygoroutines + channelsfibers + a keyword
Compile speedfastU → C → GCC
Null safetynil panics at runtime+N compile-time checked

The transpilation target

U was designed to analyze PHP codebases. The transpiler converts PHP to U with source maps, so every finding references the original PHP line.

PHP
<?php
class UserController {
    public function uploadAvatar(
        Request $request
    ): JsonResponse {
        $file = $request->file('avatar');
        Storage::disk('s3')->put(
            'avatars/'.$request->user()->id,
            $file
        );
        Mail::to($request->user())
            ->send(new AvatarUploaded);
        return response()->json(['ok'=>true]);
    }
}
U
d UserController
    f upload_avatar(req: Request)
        -> JsonResponse +IO +Net
        file = req.file("avatar")
        Storage.disk("s3").put(
            `avatars/{{req.user().id}}`,
            file
        )
        Mail.to(req.user())
            .send(AvatarUploaded())
        r => response().json({ok: true})

The U version reveals +IO (Storage) and +Net (Mail) in the signature. PHPStan can't see this — the capabilities flow through Laravel facades.

Strings — PHP's three-way confusion

PHP
$name = 'Greg';           // no interpolation
$msg = "Hello, $name!";   // interpolation
$html = "<<<HTML
<div class="box">$name</div>
HTML;                      // heredoc
// which one escapes HTML? none of them
U
name = "Greg"                // S, literal
msg = `Hello, {{name}}!`     // S, interpolation
html = HTML`<div class="box">
    {{name}}
</div>`                      // Formats.HTML — escaped

Floating-point money

0.1 + 0.2 != 0.3 in PHP. Every e-commerce site using float for prices has rounding bugs. The workaround (integer cents) loses expressiveness. U's Q type stores exact rationals.

PHP
$a = 0.1 + 0.2;
var_dump($a == 0.3); // bool(false)
// Workaround: use integer cents everywhere
$price = 1999; // $19.99 as cents
U
a: Q = 0.1 + 0.2
a == 0.3              // true — exact rational
price: Q = 19.99
price / 3             // exact: 6.663333... (stored as ratio)

SQL injection is a type error

The Equifax breach (147 million records) was SQL injection. PHP still lets you concatenate strings into queries. Prepared statements help, but nothing stops the developer from using string concat. U makes it a compile error — the type system rejects S where Formats.SQL is required.

strlen vs mb_strlen — the encoding trap

PHP has two string worlds: byte-oriented (strlen, substr, strpos) and Unicode-aware (mb_strlen, mb_substr, mb_strpos). Use the wrong one and your app breaks on non-ASCII input. Every function call is a choice, and the wrong choice is always silent.

PHP
$s = "café";
strlen($s);     // 5 (bytes, wrong!)
mb_strlen($s);  // 4 (characters, right)
substr($s, 0, 4);    // "cafÃ" (broken!)
mb_substr($s, 0, 4); // "café" (correct)
U
s = "café"
s.length          // 4 (characters, always)
s.slice(1, 4)     // "café" (always correct)
// One function. Always Unicode-aware.
// No mb_ prefix. No choice to get wrong.

Memory model — U is what PHP should have been

PHP's killer feature: per-request memory. The process handles a request, frees everything, handles the next. No memory leaks. No accumulation. The downside: the process restarts or at least reinitializes for each request — expensive connection setup, no shared state, no long-running workers.

U's webserver uses arena allocation per request — the same model, but the process stays alive. Connections stay open. State persists across requests. The arena (request-scoped allocations) resets in microseconds between requests. You get PHP's memory safety with a long-running server's performance.

PHP
// Per-request lifecycle:
// 1. New process/thread
// 2. Load framework (50-200ms cold)
// 3. Handle request
// 4. Free everything
// 5. Repeat
// No memory leak — but no shared state,
// no persistent connections, expensive.
U
// Arena per request:
// 1. Process stays alive (connections warm)
// 2. Arena: request-scoped allocations
// 3. Handle request (arena + stack)
// 4. Arena.reset() — microseconds
// 5. Shared state persists across requests
// PHP's safety + server's performance.

PHP transpiles to U today. Upload your PHP codebase — the transpiler converts it with source maps. Every finding references your original PHP line. Try it in the playground →

What transpilation gives you — without rewriting

1. Capability inventory

The analyzer walks your entire codebase and tells you what each module does: which files touch the database, which make network calls, which use the filesystem. PHPStan can't do this — the capabilities flow through facades and service containers that are opaque at the PHP syntax level. The transpiler flattens them. Storage::disk('s3')->put(...) becomes Storage.disk("s3").put(...) and the analyzer tags it +IO.

2. Cross-file data flow

A value enters through $_GET['id'], passes through three controllers, and ends up in a query. Is it sanitized? The analyzer traces it through the transpiled U graph with source maps back to every PHP line it touches. One report, not hours of manual tracing.

3. Supply chain diffing

When a Composer dependency updates, the analyzer diffs the capability graphs. If version 2.3 had [+DB] and version 2.4 has [+DB, +Net, +Exec], that's a red flag — traced to specific lines in the new version.

4. LLM-readable audit surface

An LLM reading your PHP sees $this->repository->save($entity) and has to infer that it touches a database. The same code in U has +DB on the function signature. The LLM reads the signature, not the implementation. Audit time drops from hours to minutes.

5. Proven at scale

The full Qbix Platform — 2,359 PHP files, 389,083 lines — transpiles to U at 100% success. Zero errors. 75.5% of modules are pure computation. The capability breakdown is a one-page summary of what 389K lines of PHP actually do.

FeaturePHPU
Type safetygradual typing (PHPStan)full static types
SQL injectionstring concat still commonSQL`` parameterizes, S rejected
XSSmanual htmlspecialchars()HTML`` auto-escapes
Capability trackingnone (PHPStan can't do it)transpile → analyze → report
Unicode stringsmb_* functions (optional)always UTF-8, all methods
Null safety?Type (PHP 8.0+)+N on any type, no NPE
Transpile to U2,359 files, 389K lines, 100%

Type safety

JavaScript has no types. TypeScript adds them but they're erased at runtime. U's types compile to real C types.

JavaScript
function add(a, b) { return a + b; }

add(1, 2)         // 3
add("1", 2)       // "12" (silent coercion)
add(null, [])     // 0 (wat)
add({}, [])       // "[object Object]"
U
f add(a: I, b: I) -> I
    r => a + b

add(1, 2)         // 3
add("1", 2)       // compile error
add(none, [])     // compile error

Async/await

JavaScript
async function fetchAll(urls) {
    const results = await Promise.all(
        urls.map(async (url) => {
            const res = await fetch(url);
            if (!res.ok) throw new Error(
                `HTTP ${res.status}`
            );
            return await res.json();
        })
    );
    return results;
}
U
a f fetch_all(urls: [S]) -> [Tree] +Net
    r => urls
        .map(url => a Http.get(url).json())
        .all()

U's a keyword replaces async/await/Promise. The compiler manages fibers. .all() joins concurrent results.

Template strings

JavaScript
// JS template literals: no type safety
const query = `SELECT * WHERE id = ${id}`;
// SQL injection — id is string-concatenated

const html = `<p>${userInput}</p>`;
// XSS — userInput is not escaped
U
// U tagged templates: type-safe by construction
query = SQL`SELECT * WHERE id = {{id}}`
// id is a parameter, never in the SQL string

html = HTML`<p>{{user_input}}</p>`
// user_input is HTML-escaped automatically

Event listeners leak memory

React needs useEffect cleanup. jQuery needs .off(). Angular needs ngOnDestroy. Forget once, memory leak. U ties subscriptions to an owner lifecycle — when the owner dies, all its subscriptions are automatically removed.

JavaScript (React)
useEffect(() => {
    const handler = () => update();
    window.addEventListener('resize', handler);
    return () => {
        window.removeEventListener(
            'resize', handler
        ); // forget this = memory leak
    };
}, []);
U
e(window).on(
    (ev: ResizeEvent) => update(),
    { owner: t }
)
// When t dies, subscription removed.
// No cleanup. No ceremony.

State management without Redux

React needs Redux, Zustand, or Jotai. Vue needs Pinia. Every framework has a state management ecosystem. U's MVCC operator (<<) IS the state management — language primitive, not a library.

JavaScript (Redux)
// 2,000 lines of Redux boilerplate:
const slice = createSlice({
    name: 'user',
    initialState: { name: '', loading: false },
    reducers: {
        setName: (state, action) => {
            state.name = action.payload;
        },
        setLoading: (state, action) => {
            state.loading = action.payload;
        }
    }
});
U
// Built into the language:
state << { name: new_name, loading: false }

// Subscribe:
e(state).on(
    (st: AppState) => render(st),
    { owner: t }
)

GC pauses in the browser and in Node

V8's garbage collector pauses JavaScript execution. In the browser: dropped frames, janky scroll. In Node: p99 latency spikes. The workaround (object pooling, manual cleanup) is exactly the manual memory management JavaScript was supposed to free you from.

AI-generated code — trust but can't verify

Copilot writes JavaScript that looks correct. No type system catches the bugs. U's u f keyword marks AI-generated function bodies — the compiler verifies them against the full typed contract. Types, capabilities, error types, intent tags. If the body violates the contract, it doesn't compile. The type system IS the review.

JavaScript
// Copilot suggests:
function search(query, items) {
    return items.filter(i =>
        i.name.includes(query));
}
// Looks fine. But: query could be null.
// items could be undefined. i.name could
// be missing. No type system catches it.
U
/// @intent Rank by relevance
/// @constraint Handle empty query
/// @security Never expose internal IDs
u f search(query: S, items: [Product])
    -> [Product]
// LLM generates body. Compiler verifies
// types, capabilities, error handling.
// Contract violation = compile error.

JavaScript transpiles to U today. The tree-sitter based transpiler handles modern JS with source maps. Try it in the playground →

What transpilation gives you

1. Types your TypeScript can't check

TypeScript checks types but erases them at runtime. It can't check capabilities — whether a function touches the network, the filesystem, or the DOM. Transpile to U and the capability analysis fills the gap TypeScript leaves.

2. Cross-language consistency

A full-stack app has JS on the frontend and PHP (or Python) on the backend. Today, each is analyzed separately — ESLint for JS, PHPStan for PHP. Transpile both to U and the analyzer builds one graph: a value flowing from a JS fetch() through a PHP API endpoint to a database query is one data-flow path, traceable in both languages.

3. Every npm dependency, analyzed

When you npm install a package, you trust it. The analyzer transpiles the package to U and produces a capability report: this package uses [+Net, +IO] — or it doesn't. A package that claims to be a "string utility" but has +Net in its capability graph is suspicious. The analysis catches it before your code review does.

4. LLM audits see structure, not callbacks

JavaScript's callback chains, promise chains, and event-driven patterns make code hard for LLMs to follow. Transpiled U is flat: function signatures declare capabilities, types are explicit, control flow is visible. An LLM auditing the transpiled form catches issues the original callback spaghetti would hide.

FeatureJavaScriptU
Type safetynone (TS erased at runtime)compiled to C types
SQL injectiontemplate literal string concatSQL`` parameterizes
XSSmanual escapingHTML`` auto-escapes
Null/undefinednull, undefined, NaN, 0, ""+N (one concept: none)
Capability trackingnonesix categories
PerformanceJIT (V8)native binary via C
Transpile to Utree-sitter based, source maps

Type hints vs real types

Python's type hints are documentation. They're not enforced. U's types are compiled to C.

Python
def greet(name: str) -> str:
    return f"Hello, {name}!"

greet(42)  # runs fine! type hint ignored
greet(None)  # runs, crashes later on .upper()
U
f greet(name: S) -> S
    r => `Hello, {{name}}!`

greet(42)    // compile error: I is not S
greet(none)  // compile error: S is not S+N

Data classes

Python
from dataclasses import dataclass
from typing import Optional

@dataclass
class User:
    name: str
    email: str
    age: int
    bio: Optional[str] = None

    def display_name(self) -> str:
        return self.name.title()
U
d User
    name: S
    email: S
    age: I
    bio: S+N

    f display_name() -> S
        r => t.name.to_capitalized()

The GIL — Python's original sin

Python's Global Interpreter Lock means only one thread executes Python code at a time. asyncio is a workaround, not a solution — it's cooperative multitasking within a single thread. CPU-bound parallelism requires multiprocessing (separate processes, expensive IPC). U has real fibers with no GIL.

Nondeterminism is invisible

Any Python function might call random.random() or datetime.now() three calls deep. You discover this when your tests are flaky. U separates determinism (±D) from effects (±E) as independent axes — the type system tells you which functions are deterministic and which aren't.

Python
def process(data):
    # Is this deterministic? Who knows.
    # It calls helper() which calls
    # another_helper() which calls
    # random.choice(). Good luck finding
    # that in a 50-file codebase.
U
f process(data: S) -> Result     // -D: deterministic
f process(data: S) -> Result +D  // +D: nondeterministic
// The modifier propagates: if you call
// a +D function, yours becomes +D too.
// The compiler catches it.

Copy vs reference — Python's mutable default trap

Python
def add_item(item, lst=[]):  # trap!
    lst.append(item)
    return lst

add_item("a")  # ["a"]
add_item("b")  # ["a", "b"] — shared list!
U
f add_item(item: S, lst: [S]) -> [S]
    // lst is -M (read-only) by default
    // lst.append(item) → compile error
    // must explicitly c lst to copy it
    result = c lst +M
    result.append(item)
    r => result

Documentation drifts from code

Python docstrings rot. Sphinx docs are out of date the week they ship. Type hints in docstrings contradict actual types. U's UDoc derives 125 mechanical facts directly from the AST — reads, writes, guards, throws, calls, complexity. It can't drift because it's computed from the source on every build.

Performance — 100x slower is not a rounding error

Python is 10-100x slower than C for CPU-bound work. PyPy helps (2-5x), Cython helps (10-50x), but both are separate tools with their own limitations. U compiles to C, then to a native binary. The same code that's readable is also fast.

Python
# Matrix multiply — pure Python:
# ~500 seconds for 1000x1000
# NumPy (C underneath): ~0.5 seconds
# You're not writing Python for speed.
# You're calling C libraries from Python.
U
// Matrix multiply — native U:
f matmul(a: [[R +V]], b: [[R +V]])
    -> [[R +V]]
// Compiles to C with SIMD via +V.
// Same speed as hand-written C.
// No NumPy dependency. No FFI boundary.

Python transpiles to U today. Tree-sitter based, with source maps back to your original .py lines. Try it in the playground →

What transpilation gives you

1. Types that are actually enforced

Python's type hints are documentation. mypy is optional and not everyone runs it. Transpile to U and the types become real — compile errors, not suggestions. Every function gets a typed signature derived from its Python source, and the U compiler checks every call against it.

2. Capability analysis on ML pipelines

A data science pipeline reads CSV files, queries a database, calls an API, trains a model, and writes results. Which step does what? Transpile to U and each function's capabilities are visible: +IO on the file readers, +DB on the query functions, +Net on the API calls. The pipeline's side-effect structure is a one-page report.

3. Dependency audit without reading code

PyPI packages execute arbitrary code at install time (setup.py). Even after install, a package might phone home, exfiltrate data, or install a backdoor. Transpile the package to U: the capability report shows exactly what the code can do. A "math utility" with +Net is immediately suspicious.

4. LLMs understand U better than Python

Python's dynamic typing means an LLM reading def process(data) knows nothing about what data is, what the function returns, or what side effects it has. The transpiled U has f process(data: DataFrame) -> Result +DB +IO — every fact the LLM needs in one line. Audit accuracy goes up because the LLM isn't guessing types.

FeaturePythonU
Type enforcementhints only (mypy optional)compiled, enforced
Performanceinterpreted, slownative binary via C
Capability trackingnonesix categories
Null safetyNone is everywhere+N explicit, checked
ConcurrencyGIL (asyncio workarounds)real fibers, no GIL
Indentationenforcedenforced (tabs)
Transpile to Utree-sitter based, source maps

What every comparison has in common

Every language above lacks one thing U provides: capability tracking in the type system. No other language can tell you, from the function signature alone, whether a module touches the filesystem, the network, a database, or the process table. U can. And because U is a transpilation target, you don't have to rewrite your code to get this — transpile your PHP, JavaScript, or Python codebase, and U's analysis tells you what every module does.

The other constant: LLM readability. When an AI audits your code, it reads function signatures. A U signature like f process(req: Request) -> Response +DB +Net tells the auditor everything it needs in one line. A C signature like int process(Request *req) tells it nothing — the auditor has to read the entire implementation, follow every call chain, and hope it doesn't miss a system() buried in a helper function.

U doesn't replace these languages. It analyzes codebases written in them. Try the playground — paste PHP, JavaScript, or Python and see U output with source maps. The transpiler converts your existing code to U. The analysis runs on U. The results reference your original source lines. You keep writing PHP, JavaScript, or Python. U works behind the scenes to tell you what your code can do.