Paradigm system
Each source file declares which paradigms it uses with @paradigm directives. Those directives unlock the builtins, syntax, and keywords tied to each paradigm. They do not affect which functions from other files can be called. Only builtins and syntax are gated, and only within the current file.
This page is normative. For a walkthrough with worked examples, see the paradigms guide.
Directives
Section titled “Directives”@paradigm <name> appears at the top of the file, before declarations, alongside @import directives. It can be repeated to stack paradigms.
@paradigm functional@paradigm procedural
@import std.ioImports are independent of paradigm directives. Importing a module does not grant any paradigm; the two systems do not interact. See Source files for directive placement and import syntax.
What each paradigm unlocks
Section titled “What each paradigm unlocks”| Directive | Unlocks |
|---|---|
@paradigm functional | map, filter, reduce, fold, foreach, do notation, monad keyword, pure function enforcement |
@paradigm procedural | for, while, do while, mut variables |
@paradigm oop | interface, composition syntax |
Some features that might look paradigm-gated are not:
- Structs are plain data containers available across all paradigms, not gated by
@paradigm oop. Methods can be associated with structs throughimpl. - Lambdas are not gated. The functional builtins take lambdas, but lambdas themselves are available everywhere.
spawn,join, andsubmitare always-available builtins, gated behind no paradigm, likeallocandread_file.
The full list of always-available builtins is in Builtins. The gated constructs are covered in detail in Functional concepts and Object-oriented concepts.
Stacking
Section titled “Stacking”Directives stack. The set of available builtins and syntax is the union of all declared paradigms. This program uses a functional builtin and procedural mutation in the same file:
@paradigm functional@paradigm procedural
func main() -> int32 { nums: int64[] = [1, 2, 3, 4, 5] doubled := map(nums, lambda (n: int64) -> int64 { return n * 2 }) mut sum: int64 = 0 for x in doubled { sum = sum + x } println(sum) return 0}Default behavior
Section titled “Default behavior”If no @paradigm directive is present, the file defaults to procedural. This file compiles without any directive:
func main() -> int32 { mut i: int64 = 0 while i < 3 { println(i) i = i + 1 } return 0}Cross-file rules
Section titled “Cross-file rules”- Functions and types defined in any file are paradigm agnostic. They can be called or used from any other file regardless of paradigm directives.
- Gating is per file and covers builtins and syntax. A file without
@paradigm functionalcannot callmapdirectly, but it can call a user-defined function that internally usesmap. - The check is intra-file and runs during semantic analysis. There is no link-time paradigm check, since calls through user-defined functions are never gated.
- There is no paradigm restriction on exports. An exported function or type is usable from any file regardless of either file’s paradigm directives.
Diagnostics
Section titled “Diagnostics”Using an undeclared paradigm feature is a compile error in that file. The diagnostic names the missing directive:
gated.dusk: 3:16: error: the 'map' builtin requires the functional paradigm; add '@paradigm functional'The same shape of error is reported for procedural constructs (for, while, do while, mut) and OOP constructs (interface, composition) when their paradigm is not declared, and for the monad keyword and do notation, which belong to the functional paradigm.