Refingere is a statically typed language made for web development first. A server, the app that runs in the browser, and the code they share are written in one language, checked by one compiler, and tested the same way - and the same language works just as well for command-line tools and everything else. It tries to stay out of your way, with a small number of ideas used everywhere: values instead of exceptions, a context instead of globals and hand-threaded parameters, and attributes instead of frameworks. It also comes with the things most programs end up needing - tables, graphs, vectors, dates, regular expressions, HTTP - so a program rarely starts by choosing libraries.
This page is a quick tour of the whole language. Each part links to the page that covers it in depth, and to an example that uses it for real.
A few things are already visible here:
global definitions are reached through a path in backticks. &stdout, and the arguments come from &args. Names starting with & are context values, and the test replaces both with inject - no mocking library, no parameters added for testing. @main is the program's entry point and @test marks a test, which fails when it ends in an error. See the Attributes reference. let introduces a name and set changes it, so every change is visible. ≔ (or :=) binds with an inferred type, : Type ≔ with a written one. Giving something a value is always ≔ - defaults, named arguments and fields too - so = is free to mean equality. The numeric types are exact about their size - U8 to U256, S8 to S256, F16, F32 and F64 - with Complex, Quaternion, Vector and Matrix alongside. The Types reference lists them all, with every method.
Parameters can have defaults and be passed by name. Lambdas are written λ(x) ... (or lambda(x) ...), and the collections take them everywhere: map, filter, groupBy, sortedBy. A function whose body contains yield is a generator, returning a lazy Iterator - see Generators.
if always has then, and its branches are a single expression or a block. for takes while, until, if and skipping modifiers, and break/continue name the loop by its variable. match tests values, types, struct fields, enum cases, tuples and errors, with an optional if guard. The exact grammar of each is in the Syntax reference.
Structs hold named fields, enums are one of several cases - each empty, with positional values, or with fields - and interfaces list what a type promises, given by an implement block written separately from the type. Functions, structs and interfaces can be generic, and a constraint like T: HasArea says what the body can use. See Interfaces and Generics.
Types are built from parts instead of inherited. && combines structs' fields and || combines enums' cases - naming a struct there makes a case carrying its fields. The single operators are different: in a type, A & B is a tuple and A | B an untagged union, with no cases of its own - its members are told apart by their type. See Types.
There are no exceptions. A function that can fail says so in its type - Error ! U8 is an error or a U8 - and the caller decides what to do, with a few operators: !! passes the error up, !? replaces it, and match takes it apart - while ?? and ?! do the same for a value that's merely undefined. error message with value attaches data to an error. See Errors.
A context value is read with &name, and an inject provides one for everything called from the rest of its block - however deep. The builtin ones cover everything a program touches outside itself: &stdout, &stderr, &stdin, &fs, &env, &args, &clock, &random, &logger, &heapAllocator, &panicHandler - and plain values such as &hashSeed, what every hash starts from.
That's what makes code testable without changing it: a test injects a MemoryFileSystem, a FixedClock, a SeededRandom or a TrackingAllocator, and the code under test never knows. Your own dependencies work the same way, injected for a file, a folder or the whole project with @inject, @injectLocally or @injectGlobally. See Context and Word Count.
Math notation works as written: √, superscript powers and subscript indexing - see Numbers.
Other languages are embedded as macros - #SQL, #HTML, #CSS, #WGSL, #GLSL, #HLSL, #MSL, #SPIRV, #SVG, #JSON, #YAML, #TOML, #CSV, #REGEX, #MARKDOWN, #SHELL, #VUE - each checked by its own parser when the program compiles. Values go in through #{ }, always escaped the way that language needs, so injection attacks can't happen by construction. #comptime(...) moves work to compile time - see Compile-time Code.
The same functions and structs become a server or a user interface through attributes: @serve and @get/@post/... turn functions into HTTP routes, and @app, @component and @state build an app that runs in the browser. Either way, the code stays ordinary functions and structs, so tests call them directly. See HTTP Server and Webapp.
This is what Refingere is built for first. Programs compile to WebAssembly, which every browser runs, and WASI runtimes such as wasmtime run the same modules on a server or the command line - see Compiling to WebAssembly. They compile to JavaScript too, as an ES module - see Compiling to JavaScript.