Scalars & Strings
Nomi has three numeric scalar types — Int,
Float, Decimal — plus
Bool and String.
Numbers, booleans, operators
Section titled “Numbers, booleans, operators”Arithmetic, comparisons, and the logical operators and / or / ! all read
as you’d expect:
fn main(): Bool {
dbg 42 + 8
dbg 10 - 3
dbg 2 * 21
dbg 20 / 4
dbg -(3 + 4)
dbg 42 > 10
dbg True and !False
}
Number literal forms
Section titled “Number literal forms”Numbers can use digit separators, alternate radices (hex / binary / octal), or scientific notation — different spellings of the same values:
fn main(): Float {
dbg 1_000_000
dbg 0xFF
dbg 0b1010
dbg 1.0e10
}
Decimal — exact arithmetic
Section titled “Decimal — exact arithmetic”Float is fast but approximate: small rounding errors accumulate, and
familiar identities like the one below quietly fail. Decimal is the exact
alternative — use it whenever rounding error is a bug (money, billing,
anywhere correctness matters more than speed). Decimal literals carry a d
suffix:
fn main(): Decimal {
// Float carries base-2 imprecision
dbg 0.1 + 0.2 == 0.3
// Decimal is exact
dbg 0.1d + 0.2d == 0.3d
// Sum four prices to the cent — exactly
dbg 19.99d + 5.00d + 2.50d + 0.99d
}
Strings — interpolation and concatenation
Section titled “Strings — interpolation and concatenation”Strings interpolate ${expr} and concatenate with +:
import std/io: IO
fn main() {
name = "Nomi"
IO.print("Hello, ${name}!")
IO.print("1 + 2 = ${1 + 2}")
IO.print("Hello" + ", " + "Nomi")
}
IO.print accepts any value that can be displayed, so
you rarely need to stringify before printing — interpolation calls each value’s
Display.to_string for you.
+, -, *, and / are backed by standard operator interfaces:
Add, Subtract,
Multiply, and Divide. The
built-in scalar impls cover numeric arithmetic; + also covers string
concatenation. Custom types can implement the same interfaces when the operator
is the clearest domain operation.
Text model
Section titled “Text model”Nomi’s text model has three pieces:
Stringis text. Nomi has no separateChartype, so even one-character text is aString.- A grapheme is one user-visible character.
éand many emoji count as one grapheme even when they are built from multiple Unicode values. Nomi represents graphemes asStringvalues. Codepointis an integer-backed Unicode scalar value: a valid number in Unicode’s scalar-value range. Use it when you need to inspect the lower-level values that make up text.
Most string operations use the grapheme view: String.length,
String.graphemes,
String.slice, and
String.reverse work in user-visible
characters rather than raw bytes.
Use String.codepoints when you need the
lower-level scalar values:
fn main(): List<Int> {
dbg String.length("café")
dbg String.graphemes("café")
"café"
|> String.codepoints()
|> Iter.map(Codepoint.to_int)
|> Iter.to_list()
|> dbg
}
String equality is byte-based, so visually identical text can compare unequal
when it uses different Unicode forms. Normalize with
String.normalize before comparing text
from mixed sources.
Triple-quoted strings
Section titled “Triple-quoted strings”A """…""" literal spans multiple lines. Interpolation works the same way,
and leading indentation common to every line is stripped:
import std/io: IO
fn main() {
name = "Alice"
query = """
SELECT *
FROM users
WHERE name = '${name}'
"""
IO.print(query)
}
The next chapter — Collections — uses these scalar values inside lists, maps, and sets.