Skip to content

Feature Index

Paul Squires edited this page Nov 9, 2026 · 30 revisions

Feature Index

Every language feature and library that ships with tiko, one row each, with the page that documents it. Compatibility lists the known compatibility differences.

Language

Feature What it is
Properties Auto-properties, Declare Property Age As Long = 18 Get, Private Set: a hidden field and both accessors written by the compiler, with Get, Init for a value only the constructor sets. Property blocks hold Get and Set in one definition, with value as the value crossing the boundary. Any number of indices, Static properties, properties in a namespace, += and Swap on a property, and a property passed ByRef is written back through its setter.
Generics type Box( of T ) and function Max( of T )( … ). One concrete type per distinct type-argument list, type-checked like any other type. Not a macro, not a separate template language.
Interfaces interface IShape … end interface, class Circle with implements IShape lines. A contract any type or class can take on, as many as it needs, with each method mapped explicitly; an interface value is a 16-byte reference, and a plain type needs no vtable to implement one. Generic interfaces, and constraints: function Largest( of T as IComparable( of T ) ).
Iterator Protocol A structural contract: a type is iterable if it has GetIterator( ) returning something with IsValid( ), Value( ) and MoveNext( ). No base class, no interface, no registration, nothing to inherit.
For Each Walks arrays (any lbound), var-len STRING, and any type satisfying the protocol.
Tuples as (int32, ustring) — an anonymous aggregate identified structurally, so (int32, ustring) is one type in every module. Gives tiko multiple return: function MinMax( ) as (min as int32, max as int32), then var (lo, hi) = MinMax( ).
Array Results function foo( ) as integer( ) returns a classic dynamic array, not a container: return { 10, 20, 30 }, a local array (moved out) or another call, then var i = foo( ) and i( 1 ). The caller owns it; nothing is copied or leaked.
Collection Initializers dim b as containers.Array( of integer ) = { 10, 20, 30 }: a brace list on a type that needs construction calls its Add, PushBack or Push once per element, after the constructor. { {"a", 1}, {"b", 2} } fills a Map. Works for any type with one of those methods, not only the containers.
Defer defer <statement> runs on the way out of the enclosing scope, in reverse order, on every path — fallthrough, exit, return, goto out of nested scopes — and every iteration of a loop body. A contextual keyword: existing code using defer as a name keeps working.
Lambdas function( byval x as int32 ) as int32 … end function in expression position. Non-capturing ones are plain procedure pointers, so every existing callback API — including C ones like qsort — works unchanged. Capturing ones build a stack closure; every capture states byval or byref explicitly.
Block Strings """ ending its line opens a multi-line string literal; the closing line's indentation is removed from every line, so the text indents with the code around it. " needs no escaping, line breaks are always LF, and !""" reads escapes.
Partial Types partial type and partial class write one type in several blocks, usually two files of one module, so a tool's generated half and a person's half never edit each other's text.
String Slices s[lo to hi] names part of a string without copying it: uslice, slice and wslice view a ustring, a string or a wstring in place, print and compare like text, and become an owned string only when assigned to one. The compiler tracks where each slice came from: a slice cannot outlive its text, be stored where it could, or be read after its string grew — and -exx checks what crosses a procedure. std/strings adds Find, TrimSpace and SplitSlices over slices.
Embed #embed NAME "file" puts a file's bytes inside the program at compile time, as a read-only ubyte array with a NUL after the last byte. The path is searched like an #include; the file is a build dependency; public #embed exports it from a module.
Typed Variadics sub Log( items() as const ustring ... ) takes any number of arguments, each converted to the element type -- through its constructors too -- and packed into an array the procedure reads like any array parameter. A whole array of that type passes as the list, with no copy. A call that matches without packing always wins, so adding one never changes an existing call.
UString A dynamic Unicode string that is UTF-16 on every target, unlike WSTRING (2 bytes on Windows, 4 on Linux and macOS, 1 on DOS). A true intrinsic type in the compiler, not a library — LEN is O(1) and every string intrinsic works. const G as ustring = "hé" declares a ustring constant, read as a ustring everywhere: len( G ) is 2.
Fixed-Width Types int8, uint8, int16, uint16, int32, uint32, int64, uint64, float32 and float64: names that say their width, for byte through double. Typedefs, not new types, and not keywords — a program's own declaration of one of the names wins.
Nullptr nullptr, a null pointer that is a pointer: an any ptr constant, so an overload, var and a generic see a pointer where 0 gives them an integer. A predeclared constant, not a keyword — a program's own nullptr wins — and 0 still works.
Goroutines and Channels spawn Worker( a, b ) runs a procedure concurrently with its arguments copied at the spawn point. Channel( of T ) is a typed queue — rendezvous or buffered, with a close protocol and for each v in ch. select channel waits on several at once; WaitGroup joins a fan-out; Context cancels one. Start with the tutorial.

Standard library

Library What it is
Array Array( of T ) — one implementation, fully type-checked per instantiation. Push is amortised O(1); capacity doubles instead of reallocating on every append.
Map / Set / LinkedList Hash map, hash set and a doubly-linked list in the std/containers module, written in ordinary tiko-lang with zero compiler support.
OrderedMap and OrderedSet OrderedMap and OrderedSet are red-black trees: O(log n), always in ascending key order, with TryFirst/TryLast/TryFloor/TryCeiling. They need only < on the key — no hash and no =. Iterators survive insertion.
Sorting Sort( a ) is an introsort — O(n log n) worst case, in place. StableSort( a ) is a bottom-up merge sort. SortList( l ) relinks nodes and moves no element. Every move is a SWAP, so sorting an Array( of string ) performs zero string copies.
Searching BinarySearch, LowerBound, UpperBound and PartitionPoint on a sorted array; PartialSort, NthElement, NextPermutation, RotateArray, ReverseArray and Dedup in place. Each has a ...By form taking a comparer.
PriorityQueue / Deque / BitSet A binary-heap priority queue (smallest first, or any order with a comparer), a ring-buffer double-ended queue with O(1) indexing, and a growable set of bit indices with set algebra.
Optional / Result Optional( of T ) for a value that might not be there, Result( of T, E ) for a value or the reason there isn't one. Zero is a value; a default-constructed Result is a failure, so a forgotten assignment cannot read as success.
SharedPtr and WeakPtr Shared ownership by reference count, destroyed exactly once when the last handle goes. WeakPtr observes without keeping alive. Counts are atomic under -mt. Derived→base is the explicit SpCast, and the base needs a virtual destructor.
String Library 37 algorithms — Replace, Split, Join, Between, the pad family, the character-set family — in the std/strings module, working on all four string types from one call. Nothing becomes a keyword.
Format strings.Format( "{:>8} {:.2f}", name, price ): C++20 std::format's format strings and spec language -- fill, alignment, width, sign, precision, binary/octal/hex, every float form, the shortest round-trip double. A literal format string is checked at compile time against the values' types, with the caret on the character at fault. TryFormat for templates that are data; your own types by a text cast or Formattable.
Chrono Date and Time DateTime, LocalDate, LocalTime, Instant, TimeSpan, WallClock, Stopwatch, CpuClock, TimeZoneInfo — immutable value types over int64 ticks of 100 ns. ISO 8601 / RFC 3339, OS-native offsets and DST, a monotonic clock. Arithmetic never wraps. Ships alongside datetime.bi.
Codec Base64 (standard and URL-safe), base32, hex and percent-encoding, with strict decoding that reports the position of the first bad character; fixed-width numbers in either byte order and LEB128 varints in a byte buffer.
Hash CRC-32, CRC-32C, Adler-32, FNV-1a, xxHash64, MD5, SHA-1 and the SHA-2 family behind one Hasher interface: one call, or streaming in pieces; DigestFile; Hmac( of H ) over any cryptographic hasher; ConstantTimeEquals for comparing MACs.
OS os.Args( ) in Unicode, GetEnv that tells unset from empty, SetEnv/UnsetEnv/Environment/ExpandEnv, host facts (CpuCount honours affinity and containers), ExitWith/Abort, and Ctrl-C / SIGTERM as a flag you poll or wait on -- plus tasks.InterruptContext( ), a Context cancelled by the first interrupt.
Process process.Command( "git" ).Arg( "status" ) -- an argument vector, never a shell unless you ask for Shell. Run, Capture (stdout and stderr read together, so 1 MiB on both cannot deadlock), or a Child whose stdin/stdout/stderr are fs.Stream pipes; Wait, WaitTimeout, TryWait, Kill, Terminate. PATH lookup never in the current directory, .bat/.cmd arguments cmd.exe would expand are refused, and only the three stdio handles reach the child.
Net net.ParseIp, net.Resolve, TcpStream (an fs.Stream: Connect/ConnectHost with timeouts, then WriteUtf8, ReadExact), TcpListener (Bind, Accept with a timeout), UdpSocket (SendTo/ReceiveFrom, truncation reported). ParseUrl (RFC 3986), Url.Resolve for relative links, QueryValues. Strict address parsing: a leading zero in an IPv4 part is refused. Blocking calls with timeouts on both systems.
HTTP and TLS http.Fetch, http.Post, Client (Fetch/Head/Post/Send, connection reuse, redirects, TimeoutMs, MaxBodyBytes), Request (SetBody, SetBasicAuth), Response (StatusCode, Header, Text( )), Client.Open for a body read as a stream. std/tls: TlsStream (ConnectHost, Connect, Accept) over any connected stream, TlsConfig (extra roots, InsecureSkipVerify, a server identity from PEM). The system's TLS on Windows, OpenSSL 3 loaded at run time on Linux and macOS.
Config config.ParseToml / LoadToml into a TomlValue tree that keeps key order -- TOML 1.1 read, valid 1.0 written back with ToTomlText, every table rule enforced, errors with line and column -- and IniFile for INI: sections, key = value or key: value, case-insensitive names, duplicates replaced or refused. CSV writing (JoinCsvRow, CsvWriter) is in std/strings.
UUID uuids.NewV4( ) (random) and uuids.NewV7( ) (time-ordered, strictly increasing within a program); parse every common spelling; usable as a Map key and sortable.
Filesystem Library Path algebra, FileInfo, a reentrant directory iterator that works with FOR EACH, a recursive walk, whole-file I/O, mkdir -p / rm -rf / tree copy, glob, atomic replace, temp files, symlinks, volumes, file watching, memory mapping and locking. Every fallible call returns Result( of T, FsError ). Ships alongside Dir/Kill.
Math Library The std/numeric module: constants, rounding and classification, integer arithmetic and bits, parsing that can fail and formatting that round-trips, seeded random streams, statistics, Vec/Mat/Quat/Complex, and floating-point environment control.
Finance Money, and what you do with it. The two money types first, because a DOUBLE is the wrong place to keep an amount and 0.1 + 0.2 <> 0.3 says so: Currency is a LONGINT scaled by 10,000 -- eight bytes, four places, exact, the VB6 / COM CY / SQL Server money shape -- and Decimal is the .NET System.Decimal, a 96-bit mantissa with a per-value scale of 0..28. Both round half-to-even and report overflow as an infectious Invalid. Then the finance functions: interest and rate conventions with day count, the time value of money, loans and amortization schedules, cash-flow appraisal, depreciation, and portfolio returns and risk. Compounding frequency and payment frequency are separate parameters, so one code path serves both the US and Canadian mortgage conventions; growth over an integer number of periods is exact, solvers take a float64 kernel and say so; and an amortization schedule's final balance lands on exactly zero.
Logging A structured logger: levels spaced by ten, typed fields, a zero-allocation disabled path, and an event that lives on the caller's stack so it is thread-safe without thread-local storage.

Vendor packages

Libraries the toolchain ships in vendor/, each reached with one #import and nothing on the command line. Vendor Packages explains link static and link dynamic and what is inside a package.

Package What it is
sqlite3 SQLite, the embedded SQL database: #import "vendor/sqlite3", linked statically or dynamically.
SDL3 SDL 3 for windows, input, audio and 2D rendering, with vendor/SDL3/image, vendor/SDL3/ttf and vendor/SDL3/mixer for images, fonts and mixing.
raylib6 raylib, for games and graphics programs, with vendor/raylib6/raymath and vendor/raylib6/rlgl.
cJSON cJSON, a small JSON parser and writer, with vendor/cJSON/utils for JSON Pointer, Patch and Merge Patch.
PsControls Owner-drawn Win32 controls written in tiko and shipped as source, one module per control: text, numeric and date entry, buttons and switches, a list/tree, menus, toolbars, tabs, splitters, scroll bars, a message box, a colour picker, tooltips, a progress bar and a chart. Nothing underneath is a system control, so every colour is settable. PsApp runs a program of them in a dozen lines: forms with message callbacks, the message loop, a shared light/dark theme, background-thread hand-off and per-monitor DPI. Start with the tutorial.
tkControls Owner-drawn controls on SDL3 for Windows, Linux and macOS, written in tiko and shipped as source: the PsControls set under tk names, plus a code editor, a file dialog and a font list. The fonts ship with them, so a program looks the same on every system. tkApp runs a program of them: forms with event callbacks, the event loop, a shared light/dark theme, background-thread hand-off and per-monitor scale. Start with the tutorial.

Toolchain

Feature What it is
C Header Import tiko implements it through libclang, on every target, with -cimport/-cimport-out. #cinclude once "sqlite3.h" uses a C library's own header in place of a hand-written binding: the header goes to Clang, and what comes back becomes FreeBASIC declarations for the target being built — structs, unions, enums, typedefs, variadics, function pointers with their real signatures, globals and object-like macros. It emits text and lets the compiler compute the layout itself, so the two answers can be compared; a disagreement is a compile-time error naming the record and the first differing member. libclang is loaded on first use via DyLibLoad, never linked. Pairs with #foreignlib — declarations and linkage — to ship a binding entirely inside your own tree. -cimport/-cimport-out generate a deterministic binding file you can check in and diff across an SDK upgrade. Start with the tutorial.
Foreign Library Linking #foreignlib SQLITE dynamic "vendor/sqlite3" binds a name to a library shipped inside your source tree, resolved relative to the declaring file rather than the working directory, with the target variant picked by filename convention (sqlite3_windows_x86_64.dll.a, sqlite3_linux_x86_64.so). static/dynamic is mandatory — a file of the other kind is an error listing every path tried, never a silent substitution. Declarations then say lib SQLITE instead of a string literal, so a cross-platform binding stops duplicating every Declare inside #ifdef. #inclib and #libpath are unchanged. Start with the tutorial.
Vendor Packages #import "vendor/sqlite3" link dynamic reaches a C library the toolchain ships, with nothing on the command line: its header -- translated once by tiko c2t -- comes across whole and unqualified, its library is linked statically or dynamically as the import asks, and a dynamic library is copied beside the program, with a $ORIGIN run path on Linux and an @loader_path one on macOS. vendor/ is the toolchain's alone, like std/.
LLVM Backend tiko's only backend. It emits LLVM IR and runs clang -c, with its own ABI pass for Win64, SysV AMD64 and Apple's AAPCS64, and clang drives the link. It covers windows-x86_64, linux-x86_64 and macos-arm64, and there is no -gen switch. Needs clang 18 or newer: it is not bundled, and tiko takes TIKO_CLANG if set, then LLVM's usual install location, then PATH. Inline ASM is refused with TKO0407 rather than miscompiled.
tiko build tiko build, run, check, clean and metadata build a project from its tiko.toml: targets (exe, dll, lib), profiles (debug, release, your own) and paths relative to the manifest. Every main file is cached beside the modules, misses compile in parallel (-j), and a link with nothing new is skipped. --message-format json is a line-per-event stream and tiko metadata the resolved project, both versioned contracts for editors; task recipes for VS Code, Zed and Sublime.
TIKO_COMPILER tiko's identity define, always 1. #ifdef __TIKO_COMPILER__ is how a source tree asks "is this tiko?". __TIKO_VERSION__ gives the version.

Known limitations

These are the limits of the language and library today, each one measured. The headline ones:

  • Constraints name interfaces only. of T as IComparable( of T ) can be required; an operator such as = cannot. A member needing = on T makes the whole type unusable for a T without one — which is why IndexOf/Contains and everything in std/containers' sort.tiko are free procedures rather than Array members.
  • Generic procedures cannot be overloaded. One name, one generic procedure — which is why the sort library spells its variants SortBy, StableSort and SortList.
  • A capturing lambda cannot name the enclosing generic's T in its own parameter list. Non-capturing lambdas are unaffected, and so is a capturing lambda whose own parameters are concrete.
  • typeof( T ) does not see through a type parameter, so a generic body cannot branch on what T is bound to. This is why the hash contract is an overloaded HashOf.
  • Tuple variables of different element types do not convert, and there are no ordering operators on tuples.
  • No IANA tzdb in the date/time library — zones are OS-native only.
  • The filesystem library does not auto-prefix \\?\, so on Windows a path over roughly MAX_PATH is refused with FS_NAME_TOO_LONG rather than silently truncated.

The full list, with the reasoning and the measurements behind each one, is on the individual feature pages — start with Generics.

Clone this wiki locally