Language Basics
This part covers the fundamental syntax and building blocks of Dovetail.
3.1 Layout-Sensitive Syntax
Section titled “3.1 Layout-Sensitive Syntax”Dovetail uses indentation to define code blocks - no curly braces or semicolons needed. If you’ve used Python, you’ll feel right at home.
The Basics
Section titled “The Basics”- Use spaces for indentation (tabs cause a compile error)
- Four spaces is the formatter standard
- Code at the same indentation level belongs to the same block
- Lines can be continued by indenting more than the current block
Where Blocks Are Used
Section titled “Where Blocks Are Used”Indented blocks appear in many places in Dovetail:
After = in definitions:
function greet(name: String): String = let message = "Hello, $name!" message
let result = let x = 10 let y = 20 x + yAfter then and else in conditionals:
if x > 0 then debug("positive") x * 2else debug("non-positive") 0After with in match expressions:
match color with case Red => "red" case Green => "green" case Blue => "blue"After do in loops:
for item in list do debug(item)After => in lambdas:
let process = x => let doubled = x * 2 doubled + 1Single-Line Expressions
Section titled “Single-Line Expressions”For short expressions, you can keep everything on one line:
function double(x: Int32): Int32 = x * 2
if x > 0 then x else -xMulti-Line Continuations
Section titled “Multi-Line Continuations”Lines indented more than the block continue the previous expression:
let result = someFunction( arg1, arg2, arg3)Explicit Delimiters
Section titled “Explicit Delimiters”You can use explicit begin/end and ; instead of relying on layout:
// Using layout (implicit)function foo() = let x = 5 x + 1
// Using explicit delimitersfunction foo() = begin let x = 5; x + 1 endRecord Construction
Section titled “Record Construction”Record fields use semicolons as separators:
let point = Point { x = 10; y = 20 }Or with layout:
let point = Point { x = 10 y = 20}3.2 Comments and Documentation
Section titled “3.2 Comments and Documentation”Single-Line Comments
Section titled “Single-Line Comments”Use // for regular comments:
// This is a commentlet x = 5 // Inline commentDocumentation Comments
Section titled “Documentation Comments”Use /// for documentation comments. They support Markdown formatting:
/// Creates a greeting message for the given name.////// Returns a friendly greeting string that can be displayed/// to the user.////// ## Example////// ```/// greet("Bob") // "Hello, Bob!"/// ```function greet(name: String): String = "Hello, $name!"Documentation comments can be attached to:
- Functions
- Types (records, enums, classes, traits)
- Type fields
- Packages (via
README.mdin package directory)
Testing Documented Behavior
Section titled “Testing Documented Behavior”Documentation comments are displayed by tooling, but their code blocks are not
executed by dovetail test. Add an explicit test for documented behavior:
/// Adds two numbers together.function add(a: Int32, b: Int32): Int32 = a + b
test "documented addition" = assert add(2, 3) == 5The book separately checks marked complete examples in CI; see Book validation.
The synchronous debug(value) calls in examples print diagnostic output. For
application console I/O, see the async Console API.
3.3 Variables and Bindings
Section titled “3.3 Variables and Bindings”Immutable Variables (Default)
Section titled “Immutable Variables (Default)”Variables are immutable by default using let:
let name = "Alice"let age = 30
// This would be a compile error:// name = "Bob"Mutable Variables
Section titled “Mutable Variables”Use let mutable when you need to reassign a variable:
let mutable counter = 0counter = counter + 1counter = counter + 1// counter is now 2Variable Shadowing
Section titled “Variable Shadowing”You can shadow a variable by declaring a new one with the same name:
let x = 5let x = x + 1 // shadows the previous x, now x = 6let x = "hello" // shadows again, x is now a StringShadowing creates a new variable rather than mutating the existing one.
Type Annotations
Section titled “Type Annotations”Types are usually inferred, but you can add explicit annotations:
let count: Int32 = 0let name: String = "Alice"let ratio: Float64 = 3.143.4 Primitive Types
Section titled “3.4 Primitive Types”Dovetail provides the following primitive types:
| Type | Description |
|---|---|
Int8 |
Signed 8-bit integer |
Int16 |
Signed 16-bit integer |
Int32 |
Signed 32-bit integer |
Int64 |
Signed 64-bit integer |
Uint8 |
Unsigned 8-bit integer |
Uint16 |
Unsigned 16-bit integer |
Uint32 |
Unsigned 32-bit integer |
Uint64 |
Unsigned 64-bit integer |
Float32 |
32-bit floating point |
Float64 |
64-bit floating point |
Bool |
Boolean (true or false) |
Char |
Unicode character |
String |
UTF-8 encoded text |
Unit |
Empty type (like void) |
Never |
Bottom type (no values) |
Any |
Top type (all values) |
Literals
Section titled “Literals”// Integerslet decimal = 42let hex = 0xFFlet binary = 0b1010let octal = 0o755let byte = 255u8 // with type suffixlet long = 1000000i64
// Floatslet pi = 3.14159let scientific = 1.5e10let typed = 3.14f32
// Exact numbers (available from the prelude)let large = 123456789012345678901234567890biglet amount = 19.99declet exactScientific = 1.25e3dec
// Booleanslet active = truelet disabled = false
// Characterslet letter = 'A'let emoji = '🎉'let escaped = '\n'
// Stringslet greeting = "Hello, World!"let multiline = """ This is a multi-line string """
// Unitlet nothing: Unit = ()BigInt literals use the big suffix and accept integer digits in any supported
base, including 0xFFbig. Decimal literals use dec and accept base-10
integers, fractions, and scientific notation. Both are constructed exactly from
source digits, without floating-point conversion. Suffixes are lowercase and
must touch the number; digit separators are not supported.
These prelude types support +, -, *, unary -, equality, and ordering.
Operands must have the same type: write amount * 2dec, not amount * 2.
Unsuffixed literals keep their existing types, even with a Decimal annotation.
Literal patterns in match are not supported for these types; use an equality
guard instead. Division and rounding APIs are not provided yet.
Decimal uses a BigInt coefficient and a nonnegative Int32 scale. Values are
normalized: 1.200dec formats as "1.2", and -0.00dec as "0". Scientific
notation adjusts scale exactly: 1.25e3dec is 1250dec. Use
Decimal.of(123big, 2) for explicit construction, or fromInt32/fromInt64.
Negative constructor scales and arithmetic scale overflow panic. Invalid
literal scales are compile-time errors. Compiler literal construction has a
resource limit of 1,000,000 coefficient digits, including exponent expansion;
this does not limit runtime BigInt arithmetic.
BigInt and Decimal have moved from standard.math to standard.prelude;
remove their old imports. Decimal.unscaled and the first argument to
Decimal.of now use BigInt instead of Int64. RoundingMode remains in
standard.math.
The Never Type
Section titled “The Never Type”Never is special - it represents computations that never complete normally:
function fail(message: String): Never = panic messageIt’s useful for functions that always panic, unreachable code branches, and infallible async operations (Async<T, Never>).
The Any Type
Section titled “The Any Type”Any is the top type — every type is assignable to Any:
let a: Any = "Hello"let b: Any = 42let c: Any = trueTo use a value of type Any, you need to test or cast it back to a concrete type using is and as (see Type Testing and Casting).
3.5 Operators
Section titled “3.5 Operators”Arithmetic Operators
Section titled “Arithmetic Operators”| Operator | Description | Example |
|---|---|---|
+ |
Addition | a + b |
- |
Subtraction | a - b |
* |
Multiplication | a * b |
/ |
Division | a / b |
% |
Modulo | a % b |
- |
Negation (unary) | -x |
Comparison Operators
Section titled “Comparison Operators”| Operator | Description | Example |
|---|---|---|
== |
Equal | a == b |
!= |
Not equal | a != b |
< |
Less than | a < b |
<= |
Less or equal | a <= b |
> |
Greater than | a > b |
>= |
Greater or equal | a >= b |
Logical Operators
Section titled “Logical Operators”Dovetail uses keywords for logical operators to improve readability:
| Operator | Description | Example |
|---|---|---|
and |
Logical AND | a and b |
or |
Logical OR | a or b |
not |
Logical NOT | not x |
Short-circuit evaluation applies: and stops if the left side is false, or stops if the left side is true.
Bitwise Operators
Section titled “Bitwise Operators”| Operator | Description | Example |
|---|---|---|
& |
Bitwise AND | a & b |
| |
Bitwise OR | a | b |
^ |
Bitwise XOR | a ^ b |
~ |
Bitwise NOT | ~x |
<< |
Left shift | a << 2 |
>> |
Right shift | a >> 2 |
Sequence Operators
Section titled “Sequence Operators”| Operator | Description | Example |
|---|---|---|
:: |
Prepend one element to a list | x :: xs |
++ |
Join two sequences | xs ++ ys |
:: is right-associative, so 1 :: 2 :: rest prepends both in order. ++
concatenates; it works on String natively and on any type implementing the
Concat trait, including List.
let xs = 1 :: [2, 3] // [1, 2, 3]let ys = [1, 2] ++ [3, 4] // [1, 2, 3, 4]Operator Precedence
Section titled “Operator Precedence”From lowest to highest:
or(logical or)and(logical and)- binary
~(tuple extension, advanced) ==,!=(equality)<,<=,>,>=(comparison)::(cons — right-associative)|(bitwise or)^(bitwise xor)&(bitwise and)<<,>>(shift)+,-,++(additive and concatenation)*,/,%(multiplicative)not,-,~(unary prefix)- Function calls, field access, indexing,
is,as(postfix)
So x :: xs ++ ys groups as x :: (xs ++ ys), and x :: xs == ys as
(x :: xs) == ys.
Use parentheses when precedence is unclear:
let result = (a + b) * clet check = (x > 0) and (y < 10)3.6 String Interpolation
Section titled “3.6 String Interpolation”Dovetail supports string interpolation in all strings - no special prefix needed.
Simple Variable Interpolation
Section titled “Simple Variable Interpolation”Use $ followed by a variable name:
let name = "Alice"let greeting = "Hello, $name!" // "Hello, Alice!"Expression Interpolation
Section titled “Expression Interpolation”Use ${} for complex expressions:
let x = 10let y = 20let message = "Sum: ${x + y}" // "Sum: 30"
let user = getUser()let info = "User ${user.name} is ${user.age} years old"Escape Sequences
Section titled “Escape Sequences”| Sequence | Meaning |
|---|---|
\n |
Newline |
\r |
Carriage return |
\t |
Tab |
\\ |
Backslash |
\" |
Double quote |
\$ |
Literal dollar sign |
\0 |
Null character |
\u{XXXX} |
Unicode code point |
let escaped = "Line 1\nLine 2"let price = "Cost: \$99.99" // Literal $, not interpolationlet heart = "\u{2764}" // ❤Prefixed Literals
Section titled “Prefixed Literals”A string may carry an identifier on its opening quote — sql"SELECT * FROM t WHERE id = $id". The prefix hands the literal to a library, which decides what interpolation means: in a sql"..." literal, $id binds a parameter rather than splicing text. Prefixed literals also add a spread form, $..values, which exists nowhere else. See Part 24.
Multi-line Strings
Section titled “Multi-line Strings”Use triple quotes for multi-line strings:
let poem = """ Roses are red, Violets are blue, Dovetail is great, And so are you! """Interpolation works in multi-line strings too:
let name = "World"let html = """ <html> <body> <h1>Hello, $name!</h1> </body> </html> """String Concatenation
Section titled “String Concatenation”Use ++ to concatenate strings:
let first = "Hello"let second = "World"let combined = first ++ ", " ++ second ++ "!"For building strings dynamically, consider using StringBuilder from the standard library for better performance.
Summary
Section titled “Summary”You now understand Dovetail’s basic syntax:
- Layout-sensitive - Indentation defines blocks, no braces or semicolons needed
- Comments -
//for regular,///for documentation with Markdown - Variables - Immutable by default (
let), uselet mutablefor mutation - Primitive types - Integers, floats, booleans, characters, strings, unit, never, any
- Operators - Standard arithmetic/comparison, keywords for logical (
and,or,not) - String interpolation -
$variableand${expression}in any string
In the next part, we’ll explore control flow with if expressions, pattern matching, and loops.
