The object model a .NET developer recognizes instantly, the safety of Kotlin and Swift, the honesty of Rust — with one surface, one error model, one null model.
Every construct lowers to one typed IR (PIR), so the same source is fast compiled, fast as a script, and fast under debug — the three execution modes can never diverge.
Result/Ok/Err), not invisible control flow.Records are field-complete by construction — there is no zero-initialized invalid state. Errors are ordinary values:
namespace System.Billing import System.Collections public record Money(amount: decimal, currency: string) { public fn add(other: Money) -> Result<Money, BillingError> { if other.currency != currency { return Err(BillingError.CurrencyMismatch(expected: currency, got: other.currency)) } return Ok(Money(amount: amount + other.amount, currency: currency)) // checked decimal + } } public enum BillingError { CurrencyMismatch(expected: string, got: string), Overdrawn(by: decimal) }
An enum is a closed algebraic data type: a discriminant tag plus typed payloads. There is no "cast an arbitrary int in" path, and match is genuinely exhaustive — a missing arm is a compile error:
enum Payment { Card(number: string), Pix(key: string), Cash } fn describe(p: Payment) -> string => match p { Payment.Card(number) => "card ending " + number Payment.Pix(key) => "pix " + key Payment.Cash => "cash" }
Add a variant later and every non-updated match fails to compile — the compiler finds the code you forgot.
Plugfy has no method overloading — the resolution minefield is replaced by named arguments with defaults, and factory functions where construction varies. Calls read like sentences: Money(amount: 10m, currency: "BRL").
Reaching the outside world — printing, the clock, the environment, the filesystem, the network — is a capability: a named, declared, grantable permission. A capability enters the language as a bodyless host fn, the language↔world seam:
host fn print(s: string) -> int fn shout(s: string) -> int => print(s) // uses { print } fn twice(n: int) -> int => n + n // uses { } (pure) fn banner(s: string) -> int => shout(shout(s)) // uses { print } (transitive) fn apply(f: (int) -> int, x: int) -> int => f(x) // uses { * } — depends on the closure you pass
The compiler tracks every function's effect row and publishes it in the .fy manifest. Rows compose across modules under separate compilation: a library that reaches print makes every caller's row reflect that, all the way up. Capability security holds across the module boundary — the same idea as Deno's permission flags or a WASM component's imports, made first-class in the type system.
Plugfy manages memory by reference counting: deterministic release, no GC pauses, no stop-the-world. Values are freed the moment they become unreachable — predictable behavior for long-running services and tight loops alike.
Every .plug lowers to one typed intermediate representation (PIR), which targets three backends — a portable module (.fy), a native executable, and direct script execution. Same semantics, same behavior, everywhere. See Tools for the build model.