From c44eba681c0382c740d23f7fcaa75749dbf88cf3 Mon Sep 17 00:00:00 2001 From: hanez Date: Tue, 17 Feb 2026 14:34:57 +0100 Subject: [PATCH] Holiday is over, now I have fun again. No code changes. (0.38.11) --- docs/arrays.md | 197 +++++++++++++++++++++++++++++++++++++++++++++++ docs/maps.md | 203 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 400 insertions(+) create mode 100644 docs/arrays.md create mode 100644 docs/maps.md diff --git a/docs/arrays.md b/docs/arrays.md new file mode 100644 index 0000000..76c0306 --- /dev/null +++ b/docs/arrays.md @@ -0,0 +1,197 @@ +# Arrays in Fun + +This guide focuses on arrays: creation, indexing, mutation, iteration, slicing, and common gotchas. It complements the quick overview in types.md with a deeper, example‑driven treatment. + +## What is an array? + +- Ordered, zero‑indexed, mutable sequence of values. +- Can hold mixed types (numbers, strings, maps, arrays, …) in the same array. +- Bounds‑checked indexing; out‑of‑range access is a runtime error. + +## Creating arrays + +``` +// literals +a = [1, 2, 3] +b = ["alpha", "beta"] +empty = [] + +// nested +grid = [[1,2], [3,4]] + +print(typeof(a)) // "array" +print(len(a)) // 3 +``` + +Tip: Prefer square‑bracket literals for clarity and performance versus building via repeated push in a hot loop. + +## Indexing (0‑based) and assignment + +``` +a = [10, 20, 30] +print(a[0]) // 10 +print(a[2]) // 30 + +// update in place +a[1] = 42 +print(a) // [10, 42, 30] +``` + +Notes: +- Valid indices are 0..len(a)-1. Using an invalid index raises a runtime error. +- Assignment updates the existing array; references pointing to it observe the change. + +## Appending, popping, inserting, removing + +``` +a = [1] + +// append to end; returns new length +push(a, 7) // => 2, a is now [1, 7] + +// pop last element; returns the removed value +v = apop(a) // v = 7, a is now [1] + +// insert at index (shifts elements to the right) +a = [1, 2, 3] +insert(a, 1, 99) // a => [1, 99, 2, 3] + +// remove at index (shifts left) +remove(a, 2) // a => [1, 99, 3] +``` + +## Slicing and concatenation + +``` +a = [0,1,2,3,4] + +// slice(startInclusive, endExclusive) +head = slice(a, 0, 3) // [0,1,2] +mid = slice(a, 1, 4) // [1,2,3] + +// concat: join two arrays +b = ["x", "y"] +ab = concat(a, b) // [0,1,2,3,4,"x","y"] +``` + +Slicing returns a new array. The original is unchanged. + +## Iteration patterns + +``` +a = ["a", "b", "c"] + +// index‑based loop +for i = 0; i < len(a); i = i + 1 { + print(a[i]) +} + +// enumerate helper (if available in your stdlib setup) +#include as it +for pair in it.enumerate(a) { + idx = pair[0] + val = pair[1] + print(to_string(idx) + ":" + val) +} +``` + +## Copying vs. referencing + +Arrays are reference types. Assigning just copies the reference, not the contents: + +``` +orig = [1, 2] +alias = orig // points to the same array +alias[0] = 9 +print(orig) // [9, 2] + +// create a shallow copy via slice +copy = slice(orig, 0, len(orig)) +copy[1] = 7 +print(orig) // [9, 2] +print(copy) // [9, 7] +``` + +Shallow copies duplicate the top‑level array but not nested structures. + +## Equality + +``` +print([1,2] == [1,2]) // true +print([1,2] == [2,1]) // false +``` + +Array equality compares length and element‑wise equality recursively. + +## Common utilities + +Depending on your build/stdlib configuration, these helpers may be available: + +- len(a): number of elements +- push(a, v), apop(a) +- insert(a, idx, v), remove(a, idx) +- slice(a, start, end) +- concat(a, b) +- find(a, v): index or -1 +- contains(a, v): 1 or 0 + +Check your lib directory (e.g., lib/utils) for additional helpers. + +## Error handling and bounds + +``` +a = [0] +// a[1] is out of range → runtime error +``` + +Tips: +- Guard indices: if i < 0 or i >= len(a) { /* handle */ } +- Use remove/insert carefully inside loops; indices of following items change. + +## Interop with maps and strings + +``` +// arrays of maps +users = [ {"name":"Ada"}, {"name":"Lin"} ] +print(users[1]["name"]) // Lin + +// split/join patterns depend on your stdlib +#include as su // adjust if present in your tree +parts = su.split("a,b,c", ",") // ["a","b","c"] +csv = su.join(parts, ",") // "a,b,c" +``` + +## Performance tips + +- Preallocate by building from literals or chunked appends rather than one‑by‑one in very tight loops. +- Prefer index loops over repeated remove/insert in the middle of large arrays. +- Use slice to copy only when necessary; keep references for read‑only sharing. + +## Examples + +``` +// filter even numbers +src = [0,1,2,3,4,5] +dst = [] +for i = 0; i < len(src); i = i + 1 { + v = src[i] + if v % 2 == 0 { push(dst, v) } +} +print(dst) // [0,2,4] + +// flatten one level +nested = [[1,2], [3], [], [4,5]] +flat = [] +for i = 0; i < len(nested); i = i + 1 { + row = nested[i] + for j = 0; j < len(row); j = j + 1 { + push(flat, row[j]) + } +} +print(flat) // [1,2,3,4,5] +``` + +## See also + +- types.md — broader overview of core types with quick array examples. +- examples/ — many scripts operate on arrays; try play.fun to explore. diff --git a/docs/maps.md b/docs/maps.md new file mode 100644 index 0000000..316984d --- /dev/null +++ b/docs/maps.md @@ -0,0 +1,203 @@ +# Maps in Fun + +This guide focuses on maps: creation, reading/writing by key, checking key presence, iterating keys/values, nesting, and common gotchas. It complements the quick overview in types.md with a deeper, example‑driven treatment. + +## What is a map? + +- Associative container of key → value pairs. +- Keys are typically strings; values can be any type (numbers, strings, arrays, maps, …). +- Mutable: assigning with m["k"] = v updates the map in place. +- Accessing a missing key yields nil (not an error). Use guards or has(m, key). +- Maps are not ordered; rely on keys(m) if you need a concrete list of keys. + +## Creating maps + +``` +// literals +user = { "name": "Ada", "age": 37 } +empty = {} + +print(typeof(user)) // "map" +``` + +Nested structures are natural and common: + +``` +book = { + "title": "Fun Handbook", + "meta": { "pages": 120, "isbn": "123-456" }, + "tags": ["lang", "vm"] +} +print(book["meta"]["pages"]) // 120 +``` + +## Getting and setting by key + +``` +profile = { "name": "Lin" } + +// read existing key +print(profile["name"]) // Lin + +// read missing key → nil +print(profile["email"]) // nil + +// write / overwrite +profile["email"] = "lin@example.org" +profile["name"] = "Linus" +print(profile) // {"name":"Linus","email":"lin@example.org"} +``` + +Notes: +- Using a non‑string key is allowed only if your build/runtime supports it; most code uses string keys for portability. +- Missing keys produce nil. Compare against nil before converting or indexing: + +``` +v = profile["phone"] +if v == nil { print("no phone on file") } +``` + +## Checking key existence + +Use has(m, key) to check if a key is present (returns 1 or 0): + +``` +cfg = { "debug": 1 } +print(has(cfg, "debug")) // 1 +print(has(cfg, "port")) // 0 + +if has(cfg, "port") { print(cfg["port"]) } else { print("using default port") } +``` + +## Iterating maps + +Maps are not inherently ordered. To iterate, first obtain an array of keys or values. + +``` +user = { "name": "Ada", "age": 38 } + +// iterate known keys (explicit order you choose) +order = ["name", "age"] +for i = 0; i < len(order); i = i + 1 { + k = order[i] + print(k + " = " + to_string(user[k])) +} + +// discover keys from the map (order may depend on implementation) +ks = keys(user) // -> ["name", "age"] (example) +for i = 0; i < len(ks); i = i + 1 { + k = ks[i] + print(k + ": " + to_string(user[k])) +} + +// values only +vs = values(user) // -> ["Ada", 38] +for i = 0; i < len(vs); i = i + 1 { print(to_string(vs[i])) } +``` + +Tip: +- If you need deterministic output, either define the order array explicitly or sort the result of keys(user) using your available utilities before looping. + +## Copying vs. referencing + +Maps are reference types. Assigning copies the reference, not the contents: + +``` +orig = { "a": 1 } +alias = orig +alias["a"] = 9 +print(orig["a"]) // 9 + +// To make a shallow copy, rebuild from keys/values +src = { "x": 1, "y": 2 } +dst = {} +ks = keys(src) +for i = 0; i < len(ks); i = i + 1 { k = ks[i]; dst[k] = src[k] } + +dst["x"] = 7 +print(src["x"]) // 1 +print(dst["x"]) // 7 +``` + +Shallow copies duplicate only the top‑level mapping; nested arrays/maps inside are still shared unless you clone them manually. + +## Equality + +``` +print({"a":1,"b":2} == {"b":2,"a":1}) // true +print({"a":1} == {"a":2}) // false +``` + +Map equality compares sets of keys and their corresponding values for equality (order does not matter). + +## Common utilities + +Depending on your build/stdlib configuration, these helpers are commonly available: + +- has(m, key): 1 if present, 0 otherwise +- keys(m): array of keys +- values(m): array of values (parallel to keys(m) order) +- len(a) works on arrays; for maps, prefer keys(m) then len(keys(m)) if you need a count + +Check your lib or VM docs (e.g., src/vm/maps) and docs/types.md for availability and details. + +## Interop with arrays and strings + +``` +// maps inside arrays +users = [ {"name":"Ada"}, {"name":"Lin"} ] +for i = 0; i < len(users); i = i + 1 { + print(users[i]["name"]) // Ada, Lin +} + +// arrays inside maps +m = { "nums": [1,2,3] } +arr = m["nums"] +push(arr, 4) +print(m["nums"]) // [1,2,3,4] + +// JSON interop is typically via lib/io/json.fun (if enabled in your build) +#include as json // adjust to your tree and build flags +s = json.stringify({"ok":1}) // "{"ok":1}" +``` + +## Error handling and edge cases + +- Accessing a missing key returns nil. Guard before arithmetic or nested indexing. +- Mutating a map while iterating over keys you computed earlier is safe, but remember the keys array won’t update automatically; recompute if needed. +- Treat map iteration order as unspecified unless your build guarantees stability. For user‑facing output, specify/derive an order explicitly. + +## Performance tips + +- If you will access many keys, cache ks = keys(m) once and index that array in a loop rather than calling keys(m) repeatedly. +- Prefer direct writes (m["k"]=v) over repeatedly rebuilding maps in tight loops. +- When copying, copy only required keys instead of cloning the entire map if you only need a subset. + +## Examples + +``` +// merge defaults into config (without overwriting explicit keys) +defaults = { "host":"127.0.0.1", "port":8080, "debug":0 } +cfg = { "port": 9000 } + +ks = keys(defaults) +for i = 0; i < len(ks); i = i + 1 { + k = ks[i] + if !has(cfg, k) { cfg[k] = defaults[k] } +} +print(cfg) // {"port":9000,"host":"127.0.0.1","debug":0} + +// index users by id +rows = [ {"id":"u1","name":"Ada"}, {"id":"u2","name":"Lin"} ] +by_id = {} +for i = 0; i < len(rows); i = i + 1 { + r = rows[i] + by_id[r["id"]] = r +} +print(by_id["u2"]["name"]) // Lin +``` + +## See also + +- types.md — broader overview of core types with quick map examples. +- examples/ — there are example scripts involving maps; try play.fun to explore.