Functions and effects
fun double(x): number { return x * 2 } // param type inferred
fun id<A>(x: A): A { return x } // type parameter
internal fun helper(x: number): number { return x + 1 }
fun main(): number {
be twice holds fun(n: number) => n * 2 // inline lambda
be a holds 10 |> double() // pipe: 20
be b holds twice(a) + helper(1) // 40 + 2 = 42
return id(b)
}
Inference. A parameter may omit its annotation; the signature pre-pass
resolves it from the call sites. Type parameters use angle brackets
(fun id<A>(x: A): A).
Lambdas and pipes. fun(x) => e is the inline lambda. The pipe
a |> f(args) passes a as the first argument of the call. Method
chaining also reads naturally: recv.m(args) is m(recv, args). An inline
lambda captures the locals in scope around it, at any nesting depth: a lambda
written inside another sees both the enclosing lambda's parameters and the
surrounding function's locals. (A morphism lambda, move, functor,
view, reduction, is the deliberate exception: it must be closed, see
Arrows are closed.)
Modifiers. internal fun is not exported across a package boundary,
from outside, it is indistinguishable from a function that does not exist.
partial fun declares a function that may not return:
partial fun spin(n: number): number {
forever { n = n + 1 }
return n
}
Effects: visits
I/O is an effect. The principled printing path is Output.print, and a
function that uses it must say so:
fun announce(msg: String): unit visits Output {
return Output.print(msg)
}
fun run(): unit visits Output { // the effect propagates
return announce("fatto")
}
fun main(): number visits Output {
be _u holds run()
return 0
}
Calling a function that visits E requires the caller to cover E, by
declaring visits E itself, or by having a handler active. The effect
propagates up the call chain to main. This is the algebraic-effects
discipline of Yon: what a function touches is part of its signature, checked
by the compiler.
(The direct builtins IO.print_num and String.print exist as conveniences
and do not require the clause.)
Polymorphism
Yon has three forms of polymorphism, and deliberately lacks a fourth.
Parametric. Type parameters on functions: fun id<A>(x: A): A. No
constraints, no bounds, a type parameter is honest ignorance. The
Generics chapter carries this further, to parameters on
places and arrows and how a type variable anchors to the universe.
Sub-objects. place A subcontains B does not declare inheritance: it
declares a monomorphism A → B, and the checker verifies it
structurally, A must carry at least all of B's fields (subsumption),
or the mono does not exist and the program is rejected. Where it holds, an
A is usable wherever a B is expected. Each place is its own file, so the
base and the sub-object live side by side. s/Error.yon:
place Error { message number }
s/SyntaxError.yon carries the extra field and the function that reads the
base:
place SyntaxError subcontains Error { message number line number }
fun describe(e: Error): number { return e.message } // accepts any sub-object
Entry.yon builds the sub-object and passes it where an Error is expected:
place Entry { }
fun main(): number {
be s holds new SyntaxError { message 40 line 17 }
return describe(s) + 2 // 42: SyntaxError used as Error
}
Nothing is inherited, there are no methods to inherit, and no overriding,
because places have no behaviour of their own (chapter 0). subcontains is a
claim about structure that the compiler checks, exactly like a law.
Coercions along monos. The same direction appears twice more: the
comprehension {x : A where P} <: A (chapter 9) and the boolean face of
proposition (chapter 8). All Yon subtyping is travel along a declared or
derived monomorphism, never an implicit conversion.
What is missing, on purpose. There are no interfaces, traits or typeclasses, and no virtual dispatch. The Yoneda stance of chapter 0 is the replacement: a place's interface is the bundle of arrows defined on it (views, moves, reductions), and "implementing an interface" is just providing arrows, which compose, are first-class, and can carry checked laws, none of which an interface declaration gives you.