Philosophy
There is one way to do everything.
Most modern languages give you ten ways to do the same thing and then ask you to pick. Oneway picks for you. If there's a best practice, it's the only practice — and the compiler enforces it.
Alphabetical Order, Everywhere
The single most pervasive rule. Whenever ordering is discretionary, declarations must be in alphabetical order. This applies to:
- Components of a product type:
User = Birthday & Username - Variants of a union type:
Bool = False | True - Multiple methods on a type (declared top-to-bottom alphabetically)
- Arms of a
match(in the order of the union's variants) - Trait composition:
Show = Debug & PrintString - Error unions inside
Result:Result<T, IoError | NotFound> - Multiple
usestatements at the top of a file
Reordering is never a meaningful change. Diffs that only reshuffle a list do not exist. Two programmers writing the same code produce the same bytes.
Types Are the Documentation
Oneway has no local variables, no let, and no parameter names.
The shape of a function is described entirely by its types.
User.compare = (OtherUser) -> Ord {
User.Birthday.compare(OtherUser.Birthday)
}
The receiver is referred to as User (its type). The parameter is referred
to as OtherUser (its type). If you need to disambiguate two parameters of
the same type, you define a newtype — that newtype becomes the
documentation:
User = Birthday & Username
OtherUser = User
The principle: names lie, types don't. Forcing every value through a named type makes the data flow explicit and the documentation structural.
Effects Are Honest
A function's signature should not lie about what it does. print writes to
the screen, so it requires a Stdout capability — passed as an ordinary
argument from main:
String.print = (Stdout) -> Noop {
...
}
A function that does not receive a capability cannot perform the corresponding effect. No monads, no effect system — just types.
No Comments
There are no comments. Code must speak for itself through types and naming. If you find yourself wanting to write a comment, the right answer is usually to introduce a newtype or rename a method.
Batteries-Included
Oneway ships opinionated binding packages for the major application
domains — HttpServer, HttpClient, Filesystem, Database, Json,
and more — each wrapping a chosen Rust crate. The user gets a single
curated import per domain (use HttpServer, use Filesystem, …) without
having to evaluate the Rust crate ecosystem. The community is free to
publish additional bindings, but the headline batteries ship with the
language.
Under the hood, every binding is implemented in ordinary Oneway via
extern Rust declarations over its underlying crate.
There is no privileged path — anyone can write the same bindings;
Oneway just ships them so users don't have to.