Built-in types
Goblin values have runtime types; variables do not need type annotations. An operation succeeds only when its value types are compatible.
| Type | Examples | Notes |
|---|---|---|
| Integer | 0, -42 | Signed whole number |
| Float | 3.14, -0.5 | Floating-point number |
| Bool | true, false | Logical value |
| Nil | nil | Absence of a value; it prints as nil |
| String | "hello" | Immutable Unicode text |
| Bytes | Bytes("data") | Immutable raw byte sequence |
| List | [1, "two"] | Ordered, mutable collection |
| Dict | {"name": "Ada"} | Mutable key/value collection |
| Chan | Chan(0) | Channel for concurrent functions |
| Goblin | Goblin(func() {}) | Handle to a function running concurrently |
| Function | func() {} | Callable value |
Custom types are covered in Types and methods.
Numbers
Integer literals have no decimal point; float literals do. Arithmetic preserves
an integer result when both operands are integers. If either operand is a
float, the result is a float. Integer division truncates its fractional part.
The % operator returns the remainder after truncating division; the result
has the dividend's sign and is a float if either operand is a float.
print(7 / 2) # 3
print(7 / 2.0) # 3.5
print(2 + 0.5) # 2.5
print(-3 * 4) # -12
print(-7 % 3) # -1
print(7.5 % 2) # 1.5
Numbers can be compared across integer and float values. Division or modulo by zero raises ZeroDivisionError. Int() and Float() convert numbers, booleans, and numeric strings; converting a float to Int removes its fractional portion.
print(Int("42")) # 42
print(Int(3.9)) # 3
print(Float(true)) # 1
print(max(3, 5, 4))
When to convert and when to calculate
Use Int() or Float() at the edge of a program, where a value arrives as text or where an integer operation must become floating-point. Keep calculations in their natural numeric form after that. For example, parse a configuration value once, then use min() and max() to keep it within a permitted range.
var requested_workers = Int("12")
var workers = min(max(requested_workers, 1), 8)
print(workers) # 8
Int() rejects non-numeric text with ValueError. This makes it suitable for validating numeric input inside a try/catch block.
Booleans and nil
Use Bool(value) to convert any value by truthiness. False, nil, numeric zero, and empty strings, lists, dictionaries, or bytes are false.
print(Bool("")) # false
print(Bool([1])) # true
print(!nil) # true
print(true && false) # false
A function with no explicit result returns nil. Comparing any value against
nil with == is safe — it is true only for nil itself — but arithmetic and
indexing on nil are errors.
Using truthiness for optional values
Truthiness is convenient for choosing a fallback or guarding an optional collection. Use an explicit comparison with nil when zero, false, or an empty collection must still be treated as a present value.
var nickname = ""
print(nickname || "anonymous") # anonymous
var limit = 0
if limit == nil {
print("no limit supplied")
}
Strings and bytes
Strings are immutable Unicode text. They can be iterated by character,
combined with +, and repeated with *, but they are not indexable with [].
Use index() or last_index() to find a character position. See
Strings for conversions and typical methods.
Bytes are immutable raw byte sequences. Bytes("ABC") has size 3 and its first element is the integer 65. Common byte methods mirror string operations: decode(), contains(), has_prefix(), split(), replace(), and trim(). Use Bytes for raw data and strings for text.
Working with bytes
Use Bytes when reading or sending binary-oriented data, or when indexing must produce numeric byte values. Use decode() when that data should become text.
var header = Bytes("GET")
print(header[0]) # 71
print(header.contains("E"))
print(header.decode()) # GET
Lists and dictionaries
Lists and dictionaries are mutable collections. A list is ordered and uses integer indexes; a dictionary maps keys to values. Empty collections are false in conditions. See Collections for creation, indexing, and method guides.
Channels
Chan(capacity) creates a channel. Send with send(), receive with recv(), and close with close(). Capacity 0 creates an unbuffered channel. Use spawn() to start a concurrent function.
var done = Chan(0)
spawn(func() {
done.send("finished")
})
print(done.recv())
done.close()
Coordinating concurrent work
An unbuffered channel makes send() wait until another function calls recv(). This makes it useful for returning one result from spawned work. A buffered channel can accept up to its capacity before a receiver is ready.
var results = Chan(2)
spawn(func() { results.send(2 * 2) })
spawn(func() { results.send(3 * 3) })
print(results.recv() + results.recv())
results.close()
Close a channel only when no more values will be sent. Receiving from a closed, drained channel raises ValueError, rather than producing a special nil value.
Goblin handles
Goblin(function, args...) starts the function in a new goroutine and returns a handle. wait() joins it: the function's result is returned, an error it raised is re-raised, and the outcome is cached across repeated calls. done() reports completion without blocking, and wait(timeout = seconds) raises TimeoutError when the function outlives the timeout.
func square(value) {
return value * value
}
var worker = Goblin(square, 6)
print(worker.wait()) # 36
See Concurrency for how handles, channels, and spawn fit together.
Common operations
| Type | Common constructors and operations |
|---|---|
| Integer / Float | Int(value), Float(value), max(...), min(...) |
| Bool / Nil | Bool(value), !value, value && other, value || other |
| String | Str(value), size, contains(), split(), replace() |
| Bytes | Bytes(value), size, decode(), contains(), split() |
| List | List(value), size, first, last, push(), pop(), sort(), copy() |
| Dict | Dict(), get(), set_default(), keys(), items(), update() |
| Chan | Chan(size), send(value), recv(), close() |
| Goblin | Goblin(function, args...), wait(), done() |
| Function | Function(value), value(...) |
Use value.attributes() in the REPL to inspect all operations provided by a
runtime value. Constructible values also expose value.constructor; see
Built-in functions.