Syntax
This reference describes intended VKF syntax. Section 0 takes precedence over the later language direction. Examples are future compiler correction targets; this draft does not claim that every construct works in every compiler target.
Bindings and scopes
name: expression declares a binding. .name: expression updates the nearest existing binding; it does not create a missing name. Bindings may be shadowed in an inner scope, but not redeclared in the same scope. Updates describe new values; aliases retain their own previously observed values.
4 2
Indentation forms a scope. Its last expression supplies its result. Parentheses can group a scope or expression. A bare : returns the local scope as a record. Semicolons separate logical rows while preserving their indentation context. # begins a comment; :: expression writes an ordinary value.
3
Values and types
| Form | Meaning |
|---|---|
42, 1.5 | Numeric literals |
true, false | Boolean values |
null | No value |
num(2,3) | Complex value with real and imaginary components |
"text" | Immediate string |
'text' | Deferred template |
(1,2) | Tuple |
(1,) | One-element tuple; (1) is grouping |
(x:1,y:2) | Named tuple / record |
[1,2,3] | Vector |
[[1,2],[3,4]] | Rank-two vector |
[value:n] | Repeated value |
{"a":2,"b":1} | Multiset with counts |
value. | The value's actual type |
value.x, value.0 | Named or positional member |
Primitive type handles include bit, chr, int, num and str. Types are values. Type constructors describe vectors, tuples, records and multisets:
3 1 7
Unbound generic type names such as T bind from a call. Repeated occurrences require one consistent type. Shape parameters such as n bind independently. [T:n] describes a fixed vector type; [T] describes a vector without a fixed extent. Tuple structure never adds vector rank. Thus [(1,2),(3,4)]_a is rank one, while [[1,2],[3,4]]_ab is rank two.
Functions, calls and specialization
5 3 6
Parentheses call a function. Square brackets at a call site bind arguments into a callable. Binding every argument still creates a zero-argument callable; () evaluates it. A bare function name remains the unspecialized function.
11 11
Functions and operators are first-class values. Operator specialization uses named operands: +[b:2] maps a to a+2; -[a:2] maps b to 2-b. Functions may close over values, recurse and have forward declarations. Overloads use the same name with different compatible parameter types.
Runtime positional spilling uses :args, including f(:[1,2,3]) and f(:record). A declaration's ...args:T captures remaining positional arguments; :::named captures remaining named arguments. Call-site spilling uses :, not .... Process forwarding must use the final ordinary call rules; its exact variadic declaration remains undecided.
Bound arguments and structural selectors
Declare parameters in parentheses and bind ordinary named arguments with square brackets at the call site. Alphabetic suffixes select value dimensions; they do not declare or supply a separate sequence of compile-time parameters.
7 4
The former bracketed parameter declaration syntax is removed in this preview. Exact identifiers such as E_pot, x_min and particle_count take precedence where applicable. Underscores alone do not prove a structural suffix.
Named and anonymous dimensions
Named dimensions are single alphabetic characters. Digits are coordinates or values, never dimension names. Anonymous axes are positional and align only with anonymous axes. Equal extents do not make named and anonymous axes equal.
[[19, 22], [43, 50]] [1, 4]
Names align by identity. A*B has axes i,j,k; multiplication never silently contracts j. sum_j explicitly removes it. Section 0 permits repeated names in a selection such as A_ii to select equal coordinates / a diagonal; repeated structural function selectors are invalid. Distinct stored dimension identities are unique. Aligned sizes must be compatible.
Without ..., a naming suffix covers the entire vector rank. A rank-two value requires two names. One structural ellipsis explicitly retains any remaining anonymous dimensions; it may expand to zero dimensions:
| Suffix on a rank-five value | Axes |
|---|---|
_i...k | (i,_,_,_,k) |
_...ij | (_,_,_,i,j) |
_ij... | (i,j,_,_,_) |
_i...jk | (i,_,_,j,k) |
Use ASCII ...; only one structural ellipsis may occur. Anonymous dimensions align positionally among anonymous dimensions; they are not silently remapped onto named axes.
Lifting and structural calls
Ordinary functions lift over compatible argument dimensions, independently of their declarations. Tuple and record structure remains separate from vector dimensions. Whole-container overloads take precedence when they match.
[0, 2, 3]
f_i(A_ij) passes _i intact into f and lifts over _j. The selected axis does not have to disappear: the function result determines its structure. f_ij(A_ijkt) selects both i and j and lifts over the remaining axes.
Operators
| Form | Meaning |
|---|---|
+ - * / // % ^ | Elementwise arithmetic with dimensional alignment |
= ~= ~ < <= > >= | Elementwise comparisons and logical operations |
== != | Whole-value comparison |
A &_i B | Concatenate along dimension i |
A & B | Concatenate when one dimension can be inferred |
f & g | Composition: apply f, then g |
f+g, f^2 | Pointwise operations on compatible functions |
+(a,b), *(a,b) | Call an operator as a function |
=> | Implication outside a matching arm |
-> | Function / return type |
Implicit multiplication is permitted where unambiguous, such as 2x, 3(x+1) and 2sin(x). Adjacent variable names form an identifier: xy is one name, so write x*y for variable multiplication. R[L] in a domain context is a domain qualification, not implicit multiplication.
Index access and ranges
Index access uses .(). A numeric index selects a coordinate; a range selects a span. Bounds violations are errors, including an element access on an empty vector. Evaluate the base and index expressions in their defined source order.
| Index token | Meaning |
|---|---|
: | Preserve one existing dimension |
... | Preserve all remaining dimensions |
:: | Insert one size-one dimension |
For shape (3,4), a.(::,:,:) has shape (1,3,4), a.(:,::,:) has shape (3,1,4) and a.(...,::) has shape (3,4,1).
Ranges are inclusive. ..0 contains zero, ..10 contains zero through ten, a..b includes both endpoints, and .. is unbounded. Vectors are lazy by default; finite demand can consume an infinite value. Empty structural work performs no element operations; it is not an ordinary descending range.
Folds, recurrence and composition
6
reduce_i(f,A_i,initial) is an ordered fold with exactly one binary reducer, one input collection and one initial accumulator. It calls f(f(f(initial,A_0),A_1),A_2).... There is no variadic collection-fold interface. Parallel reassociation requires permission from the reducer's numerical contract.
sum_i(A)=reduce_i(+,A,0), prod_i(A)=reduce_i(*,A,1) and chain_i(F)=reduce_i(&,F,id). Empty calls yield sum()=0, prod()=1 and chain()=id. Multiple reduction selectors remove only those dimensions.
For a recurrence with several varying inputs, specialize first:
0.052808601763274413 + 0.22361225622108541i
Pipes and loop coordinates
>> passes values into a scope; $ is the current value. Flat pipes progress; indentation or parentheses nests them. Outer pipe values use $$, $$$. Selected named coordinates use $i, with outer coordinates such as $$i.
0 0 10 1 1 20
$0 is the outermost loop's iteration index; $n selects the loop at literal nonnegative integer depth n. Named and anonymous loops both count. Multiple selected axes introduce loops in selector order. Scalar scopes do not add a loop. Indices count positions from zero, even for offset or descending ranges; they are read-only and scoped. Sibling loops reuse the same depth.
Conditions, matching and errors
| Form | Meaning |
|---|---|
condition? expression | Conditional expression / indented suite |
condition?! | Assertion, optionally followed by a message |
value ?? with pattern => result | Matching expression |
| Bare final matching row | Fallback arm |
condition?> | Conditional loop |
value??> | Repeatedly evaluate and match |
@: | Return a value immediately |
@ | Return null |
@> | Continue the nearest loop / pipe |
@| | Break the nearest loop / pipe |
error! | Raise a typed error value |
expression!? | Catch errors with matching arms |
many
Error construction alone does not raise. Only error types and error values may be raised; unmatched errors continue outward. In a pipe block, @: returns from that element rather than the enclosing function.
message
Nominal types, reflection and modules
Uppercase constructors create nominal types. A spilled backing value can carry local overrides and extensions. Compatible operations reconstruct through the constructor; impossible reconstruction is an error. Distinct nominal types do not implicitly unify because their backing values happen to match.
2
Postfix . returns the actual type. For a closed record, [:record] and [:record.] return accessible field names in declaration order; (:record.) retains record field types. record.(name) selects a named field. Operators and dotted access may be overloaded with ordinary typed functions.
.name names a module handle. Bind an alias with m:.math, or spill its exports with :.math. Local modules use the same import model. Lexical shadowing does not replace an already captured qualified module handle.
Symbolic domains and calculus
x:R declares a real symbolic domain; z:C declares a complex domain. Domain information is authoritative and distinct from a variable's spelling. Ordinary evaluation, symbolic reasoning, transforms and compile-time evaluation share semantic values. simplify(f) preserves domain assumptions and singularities; executing a simplified expression requires a justified numerical policy.
f'x and f''xy are exact function-variable differentiation transforms; finite differences use separate operations such as diff_i(A). integ_x(f) and integ_xy(f) select formal variables, not structural axes. An absent differentiation dependency yields zero; integration may introduce an absent variable.
6 9
Integration parameters are ordinary callable parameters. General from and to bounds can be overridden by variable-specific bounds such as x_from and y_to. Specialization does not evaluate the integral by itself.
Physical domains and units
Domain[Dimension] qualifies a numerical domain with a physical dimension: R[L], R[L/T], R^3[M*L/T^2] and complex domains use the same mechanism. R and C alone are physically dimensionless.
The seven SI base dimensions are L M T I Θ N J. Rational compile-time exponents, such as L^(1/2), are permitted. Units contain a physical dimension, scale to canonical SI, display symbol, LaTeX symbol and optional quantity kind. Named units such as newtons do not introduce new base dimensions.
230 cm [100, 200, 300] cm
Addition, subtraction and comparison require compatible dimensions. Addition and subtraction inherit the left operand's preferred unit. Multiplication and division combine dimensions algebraically. Exponents must be dimensionless; dimensional bases normally require compile-time-known exponents.
A vector has one unit for the whole value. Conversions preserve the physical quantity and change its preferred representation; canonical SI backing remains available to computation. Unit display is automatic. Its priority is explicit .to(...), configured preference, inherited arithmetic preference, automatic SI prefix, then canonical SI. The complete configuration surface is documented when finalized; this reference does not invent additional configuration syntax.
rad and deg are physically dimensionless angle units; radians are canonical. sin, cos, exp, ln and log require dimensionless inputs. For example, exp(num(0,1)*180deg) represents Euler's relation and returns ordinary dimensionless output. Angles are not an eighth base physical dimension.
sum, mean, min and max preserve physical dimensions; variance squares them and standard deviation restores them. Differentiation divides by the variable's dimension; integration multiplies by it. Ordinary printing, LaTeX and plot labels consume the same unit information.
Strings and templates
Double quotes interpolate immediately; single quotes defer substitution. $x selects a value, $x.1f formats it, $x.0.1f accesses a member before formatting and $x.$format selects a dynamic format. .format(...) follows ordinary vectorization and accepts spilled named tuples.
[1.2, 4.6]
Geometry, views and physics
The later integration direction gives u,v,w connected-topology meanings, t time, c components and m reference-member slots. It does not override Section 0's ordinary dimension alignment and structural-call rules. Other letters are generic grouping dimensions.
p_uc, p_uvc and p_uvwc describe connected curves, surfaces and volumes. Explicit topology uses reference values: edge_bm can reference vertices grouped by i without aligning b with i. Subsets likewise carry references into a parent group; sparse properties need no dense null entries.
Properties keep their names; supplied values carry their dimensions: frame.add(p:p_ic) and world.add(p:p_ic,v:v_ic).
| Operation | Meaning |
|---|---|
set(...) | Change persistent configuration |
add(...) | Create one layer |
push() | Freeze / push the composed state |
append(...) | Add and push |
show() | Display |
plot(...) | Append and show |
Frame layers render data; world layers may evolve physical state. Laws are ordinary functions; Embeddings map state into perception. Without _t, p, v and a provide initial state. With _t, they prescribe motion. Precedence is prescribed position, velocity, acceleration, then Laws. Prescribed degrees of freedom act as kinematic constraints during physical interaction.
Camera/view configuration can vary over a grouping dimension while geometry is invariant. Styles, colormaps and interpolation are ordinary properties and functions, including structural suffixes such as jet_i and cubic_t. Side-specific limits override the corresponding side of combined limits. Symbolic plotting uses the same API; relations denote boundaries or regions and rendering adapts to geometry, zoom and discontinuities.
Event and I/O concurrency
The .concurrent library supplies process, queue, broadcast and shared. Numeric concurrency is compiler-managed; these constructs coordinate events and I/O using ordinary function call semantics.
| Form | Meaning |
|---|---|
process(worker,10,events:events) | Start an isolated concurrent function with ordinary positional/named arguments |
p.join() | Wait and return the worker result |
p.kill() | Forced termination |
queue() | Destructive FIFO queue |
q.put(value) | Send one item |
q.get() | Suspend until an item is available |
broadcast() | Publish to active subscribers |
b.subscribe() | Create a subscriber stream |
shared(value) | Share exactly one ordinary value |
Each queue item goes to one receiver. Every active broadcast subscriber receives every subsequently published value. Per-producer ordering is preserved.
This finite example uses numeric event tags. The producer runs in a separate process; the matching loop receives ordinary values until the stop tag arrives.
1 2
The receive expression is evaluated each iteration. The event remains bound in its selected arm. Shared values retain ordinary vector/tuple/record structure; published generations are immutable. Bulk-data descriptors may reference shared backing rather than copying vectors through queues.
Grammar decisions still open
Do not infer new syntax for deterministic external-resource cleanup, publishing shared generations, complete UI bindings/controls, every unit-literal and dimension-suffix precedence combination, or a final process variadic declaration. These remain explicit design decisions. Editor highlighting should use semantic resolution for suffixes and dimension identity, rather than treating every underscore name as structural.