aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2025-11-29 15:56:02 -0500
committerBruce Hill <bruce@bruce-hill.com>2025-11-29 15:56:02 -0500
commit97047cb95a88228ddefbc83b4c50b05eaf048272 (patch)
treefb6a57ef6c6fd8e18aba8b8c3e3c6bbf83d61ec6
parentd60962ab5de970a9ce0893df4154b8a0b3ea646a (diff)
Update docs
-rw-r--r--docs/bytes.md2
-rw-r--r--docs/enums.md12
-rw-r--r--docs/functions.md22
-rw-r--r--docs/integers.md31
-rw-r--r--docs/iterators.md17
-rw-r--r--docs/langs.md4
-rw-r--r--docs/lists.md46
-rw-r--r--docs/nums.md31
-rw-r--r--docs/operators.md76
-rw-r--r--docs/optionals.md9
-rw-r--r--docs/paths.md6
-rw-r--r--docs/pointers.md18
-rw-r--r--docs/reductions.md56
-rw-r--r--docs/serialization.md12
-rw-r--r--docs/structs.md23
-rw-r--r--docs/tables.md42
-rw-r--r--docs/text.md6
17 files changed, 151 insertions, 262 deletions
diff --git a/docs/bytes.md b/docs/bytes.md
index 196d5762..f18071e2 100644
--- a/docs/bytes.md
+++ b/docs/bytes.md
@@ -4,7 +4,7 @@ Byte values have the type `Byte`, which corresponds to an unsigned 8-bit
integer ranging from 0 to 255. It is generally recommended to use `Int8`
instead of `Byte` when performing math operations, however, `Byte`s are used in
API methods for `Text` and `Path` that deal with raw binary data, such as
-`Path.read_bytes()` and `Text.bytes()`. Byte literals can be written using
+`Path.read_bytes()` and `Text.utf8()`. Byte literals can be written using
the `Byte()` constructor: `Byte(5)`.
# API
diff --git a/docs/enums.md b/docs/enums.md
index 27ebdb86..85e82f36 100644
--- a/docs/enums.md
+++ b/docs/enums.md
@@ -34,10 +34,8 @@ an `else` block to handle all unmatched patterns.
Tags can also be quickly checked using the `.TagName` field:
```tomo
->> a.AnInteger
-= yes
->> a.TwoWords
-= no
+assert a.AnInteger != none
+assert a.TwoWords == none
```
## Reducing Boilerplate
@@ -62,10 +60,8 @@ func increment(arg:ArgumentType -> ReturnType)
...
->> increment(AnInt(5))
-= AnInt(6)
->> increment(SomeText("HI"))
-= Nothiing
+assert increment(AnInt(5)) == AnInt(6)
+assert increment(SomeText("HI")) == Nothiing
```
This lets us have overlapping tag names for different types, but smartly infer
diff --git a/docs/functions.md b/docs/functions.md
index 72279c11..0b836bcc 100644
--- a/docs/functions.md
+++ b/docs/functions.md
@@ -27,11 +27,9 @@ Default arguments are used to fill in arguments that were not provided at the
callsite:
```tomo
->> increment(5)
-= 6
+assert increment(5) == 6
->> increment(5, 10)
-= 15
+assert increment(5, 10) == 15
```
**Note:** Default arguments are re-evaluated at the callsite for each function
@@ -50,11 +48,9 @@ any unbound arguments, in order:
func foo(x:Int, y:Text, z:Num)
return "x=$x y=$y z=$z"
->> foo(x=1, y="hi", z=2.5)
-= "x=1 y=hi z=2.5"
-
->> foo(z=2.5, 1, "hi")
-= "x=1 y=hi z=2.5"
+func main()
+ assert foo(x=1, y="hi", z=2.5) == "x=1 y=hi z=2.5"
+ assert foo(z=2.5, 1, "hi") == "x=1 y=hi z=2.5"
```
As an implementation detail, all function calls are compiled to normal
@@ -157,10 +153,10 @@ func create_adder(n:Int -> func(i:Int -> Int))
n = -1 // This does not affect the adder
return adder
-...
-add10 := create_adder(10)
->> add10(5)
-= 15
+
+func main()
+ add10 := create_adder(10)
+ assert add10(5) == 15
```
Under the hood, all user functions that are passed around in Tomo are passed as
diff --git a/docs/integers.md b/docs/integers.md
index 6be96880..877ab39d 100644
--- a/docs/integers.md
+++ b/docs/integers.md
@@ -37,8 +37,7 @@ The simplest form of integer literal is a string of digits, which is inferred
to have type `Int` (unbounded size).
```tomo
->>> 123456789012345678901234567890
-= 123456789012345678901234567890 : Int
+i := 123456789012345678901234567890
```
Underscores may also be used to visually break up the integer for readability:
@@ -79,12 +78,10 @@ quotient := numerator / denominator
remainder := numerator mod denominator
# Modulus always gives a non-negative result:
->> remainder >= 0
-= yes
+assert remainder >= 0
# The numerator can be reconstructed sensibly:
->> numerator == denominator * quotient + remainder
-= yes
+assert numerator == denominator * quotient + remainder
```
Importantly, these invariants hold for both positive and negative numerators
@@ -95,25 +92,23 @@ negative numbers are involved. Integer division rounds _down_ instead of
rounding _towards zero_, and modulus never gives negative results:
```tomo
->> quotient := -1 / 5
-= -1
+quotient := -1 / 5
+assert quotient == -1
->> remainder := -1 mod 5
-= 4
+remainder := -1 mod 5
+assert remainder == 4
->> -1 == 5 * -1 + 4
-= yes
+assert -1 == 5 * -1 + 4
```
```tomo
->> quotient := 16 / -5
-= -3
+quotient := 16 / -5
+assert quotient == -3
->> remainder := -1 mod 5
-= 1
+remainder := -1 mod 5
+assert remainder == 1
->> 16 == -5 * -3 + 1
-= yes
+assert 16 == -5 * -3 + 1
```
# API
diff --git a/docs/iterators.md b/docs/iterators.md
index 9337d859..d1ff7b4b 100644
--- a/docs/iterators.md
+++ b/docs/iterators.md
@@ -14,15 +14,11 @@ successively gets one line from a file at a time until the file is exhausted:
line three
")
->> iter := (./test.txt).each_line()
->> iter()
-= "line one" : Text?
->> iter()
-= "line two" : Text?
->> iter()
-= "line three" : Text?
->> iter()
-= none : Text?
+iter := (./test.txt).each_line()
+assert iter() == "line one"
+assert iter() == "line two"
+assert iter() == "line three"
+assert iter() == none
for line in (./test.txt).each_line()
pass
@@ -44,7 +40,6 @@ func primes_up_to(limit:Int)
n += 1
return (n - 1)?
->> [p for p in primes_up_to(11)]
-= [2, 3, 5, 7, 11]
+assert [p for p in primes_up_to(11)] == [2, 3, 5, 7, 11]
```
diff --git a/docs/langs.md b/docs/langs.md
index 8f440932..f8784fbc 100644
--- a/docs/langs.md
+++ b/docs/langs.md
@@ -29,8 +29,8 @@ situations where a malicious user might set their username to something like
`<script>alert('pwned')</script>`.
```
->> username := Text.read_line("Choose a username: ")
-= "<script>alert('pwned')</script>"
+username := Text.read_line("Choose a username: ")
+assert username == "<script>alert('pwned')</script>"
page := $HTML"
<html><body>
Hello $username! How are you?
diff --git a/docs/lists.md b/docs/lists.md
index d12a0b5b..2700fe81 100644
--- a/docs/lists.md
+++ b/docs/lists.md
@@ -30,17 +30,15 @@ Lists can also use comprehensions, where you specify how to dynamically create
all the elements by iteration instead of manually specifying each:
```tomo
->> [i*10 for i in (3).to(8)]
-= [30, 40, 50, 60, 70, 80]
->> [i*10 for i in (3).to(8) if i != 4]
-= [30, 50, 60, 70, 80]
+assert [i*10 for i in (3).to(8)] == [30, 40, 50, 60, 70, 80]
+assert [i*10 for i in (3).to(8) if i != 4] == [30, 50, 60, 70, 80]
```
Comprehensions can be combined with regular items or other comprehensions:
```tomo
->> [-1, i*10 for i in (3).to(8), i for i in 3]
-= [-1, 30, 40, 50, 60, 70, 80, 1, 2, 3]
+nums := [-1, i*10 for i in (3).to(8), i for i in 3]
+assert nums == [-1, 30, 40, 50, 60, 70, 80, 1, 2, 3]
```
## Length
@@ -48,8 +46,7 @@ Comprehensions can be combined with regular items or other comprehensions:
List length can be accessed by the `.length` field:
```tomo
->> [10, 20, 30].length
-= 3
+assert [10, 20, 30].length == 3
```
## Indexing
@@ -61,20 +58,15 @@ last item, `-2` is the second-to-last, and so on.
```tomo
list := [10, 20, 30, 40]
->> list[1]
-= 10?
+assert list[1] == 10?
->> list[2]
-= 20?
+assert list[2] == 20?
->> list[999]
-= none
+assert list[999] == none
->> list[-1]
-= 40?
+assert list[-1] == 40?
->> list[-2]
-= 30?
+assert list[-2] == 30?
```
If a list index of `0` or any value larger than the length of the list is
@@ -103,8 +95,7 @@ has the items from one appended to the other. This should not be confused with
the addition operator `+`, which does not work with lists.
```tomo
->> [1, 2] ++ [3, 4]
-= [1, 2, 3, 4]
+assert [1, 2] ++ [3, 4] == [1, 2, 3, 4]
```
## Implementation Details
@@ -172,24 +163,20 @@ nums[4] = 40
// Constant time operation, but increments the reference count:
tmp := nums
->> tmp
-= [10, 20, 30, 40]
+assert tmp == [10, 20, 30, 40]
// Now, a mutation will trigger a copy-on-write,
// which resets the reference count to zero:
nums[4] = 999
->> nums
-= [10, 20, 30, 999]
+assert nums == [10, 20, 30, 999]
// Because of the copy-on-write, `tmp` is unchanged:
->> tmp
-= [10, 20, 30, 40]
+assert tmp == [10, 20, 30, 40]
// Since the reference count has been reset, we can do more
// mutations without triggering another copy-on-write:
nums[4] = -1
->> nums
-= [10, 20, 30, -1]
+assert nums == [10, 20, 30, -1]
```
List reference counting is _approximate_, but will only ever err on the side
@@ -211,8 +198,7 @@ nums := @[10, 20, 30]
tmp := nums
nums.insert(40)
->> tmp
-= @[10, 20, 30, 40]
+assert tmp == @[10, 20, 30, 40]
```
Having multiple pointers to the same heap-allocated list does not cause the
diff --git a/docs/nums.md b/docs/nums.md
index 1bd9b52d..7502d8bc 100644
--- a/docs/nums.md
+++ b/docs/nums.md
@@ -41,46 +41,39 @@ very liberal use of type coercion and implicit `none` checks when values are
required to be non-none. Here are a few examples:
```tomo
->> x := 0.0
-= 0 : Num
+zero := 0.0
+assert zero == 0
y := 1.0
# Division might produce none:
->> x / y
-= 0 : Num?
->> x / x
-= none : Num?
+assert zero / y == 0
+assert zero / zero == none
# Optional types and none values propagate:
->> x/y + 1 + 2
-= 3 : Num?
->> x/x + 1 + 2
-= none : Num?
+assert zero/y + 1 + 2 == 3
+assert zero/zero + 1 + 2 == none
# Optional Nums can be handled explicitly using `or` and `!`:
->> x/x or -123
-= -123 : Num
+assert zero/zero or -123 == -123
-# This would raise a runtime error if `x` and `y` were zero:
->> (x/y)!
-= 0 : Num
+# This would raise a runtime error if `zero` and `y` were zero:
+assert (zero/y)! == 0
# Assigning to a non-optional variable will do an implicit check for none and
# raise a runtime error if the value is none, essentially the same as an
# implicit `!`:
-x = x/y
+zero = zero/y
func doop(x:Num -> Num)
# If a function's return type is non-optional and an optional value is
# used in a return statement, an implicit none check will be inserted and
# will error if the value is none:
- return x / 2
+ return zero / 2
# Function arguments are also implicitly checked for none if the given value
# is optional and the function needs a non-optional value:
->> doop(x/y)
-= 0 : Num
+assert doop(zero/y) == 0
```
Hopefully the end result of this system is one where users can take advantage
diff --git a/docs/operators.md b/docs/operators.md
index fdc2aee5..02559067 100644
--- a/docs/operators.md
+++ b/docs/operators.md
@@ -25,19 +25,16 @@ between the two objects. The `<>` operator exposes this signed comparison
value to the user.
```tomo
->> 0 <> 99
-= -1[32]
->> 5 <> 5
-= 0[32]
->> 99 <> 0
-= 1[32]
+assert 0 <> 99 == -1[32]
+assert 5 <> 5 == 0[32]
+assert 99 <> 0 == 1[32]
```
It's particularly handy for using the list `sort()` method, which takes a
function that returns a signed integer:
```tomo
->> foos.sort(func(a,b:&Foo): a.length <> b.length)
+foos.sort(func(a,b:&Foo): a.length <> b.length)
```
## Reducers
@@ -50,15 +47,12 @@ the need for several polymorphic functions used in other languages like
reducers in action:
```tomo
->> nums := [10, 20, 30]
->> (+: nums)!
-= 60
->> (or: n > 15 for n in nums)!
-= yes
-
->> texts := ["one", "two", "three"]
->> (++: texts)!
-= "onetwothree"
+nums := [10, 20, 30]
+assert (+: nums)! == 60
+assert (or: n > 15 for n in nums)! == yes
+
+texts := ["one", "two", "three"]
+assert (++: texts)! == "onetwothree"
```
The simplest form of a reducer is an infix operator surrounded by parentheses,
@@ -75,8 +69,8 @@ first option is to not account for it, in which case you'll get a runtime error
if you use a reducer on something that has no values:
```tomo
->> nums : [Int] = []
->> (+: nums)!
+nums : [Int] = []
+result := (+: nums)!
Error: this collection was empty!
```
@@ -85,8 +79,7 @@ If you want to handle this case, you can either wrap it in a conditional
statement or you can provide a fallback option with `else` like this:
```tomo
->> (+: nums) or 0
-= 0
+assert (+: nums) or 0 == 0
```
The `else` clause must be a value of the same type that would be returned.
@@ -102,21 +95,17 @@ struct Foo(x,y:Int)
func is_even(f:Foo -> Bool)
return (f.x + f.y) mod 2 == 0
->> foos := [Foo(1, 2), Foo(-10, 20)]
+foos := [Foo(1, 2), Foo(-10, 20)]
->> (+.x: foos)
-= -9
+assert (+.x: foos) == -9
// Shorthand for:
->> (+: f.x for f in foos)
-= -9
+assert (+: f.x for f in foos) == -9
->> (or).is_even() foos
-= yes
+assert (or).is_even() foos == yes
// Shorthand for:
->> (or) f.is_even() for f in foos
+assert ((or) f.is_even() for f in foos) == yes
->> (+.x.abs(): foos)
-= 11
+assert (+.x.abs(): foos) == 11
```
## `_min_` and `_max_`
@@ -126,10 +115,8 @@ Tomo introduces a new pair of operators that may be unfamiliar: `_min_` and
larger or smaller of two elements:
```tomo
->> 3 _max_ 5
-= 5
->> "XYZ" _min_ "ABC"
-= "ABC"
+assert 3 _max_ 5 == 5
+assert "XYZ" _min_ "ABC" == "ABC"
```
Initially, this might seem like a fairly useless operator, but there are two
@@ -144,22 +131,18 @@ Here's some examples:
```tomo
// Get the largest absolute value number:
->> 3 _max_.abs() -15
-= -15
+assert 3 _max_.abs() -15 == -15
struct Person(name:Text, age:Int)
// Get the oldest of two people:
->> Person("Alice", 33) _max_.age Person("Bob", 20)
-= Person(name="Alice", age=33)
+assert Person("Alice", 33) _max_.age Person("Bob", 20) == Person(name="Alice", age=33)
// Get the longest of two lists:
->> [10, 20, 30, 40] _max_.length [99, 1]
-= [10, 20, 30, 40]
+assert [10, 20, 30, 40] _max_.length [99, 1] == [10, 20, 30, 40]
// Get the list with the highest value in the last position:
->> [10, 20, 999] _max_[-1] [99, 1]
-= [10, 20, 999]
+assert [10, 20, 999] _max_[-1] [99, 1] == [10, 20, 999]
```
The keyed comparison can chain together multiple field accesses, list index
@@ -173,12 +156,9 @@ This means that you get get the minimum or maximum element from an iterable
object using them:
```tomo
->> nums := [10, -20, 30, -40]
->> (_max_: nums)
-= 30
-
->> (_max_.abs(): nums)
-= -40
+nums := [10, -20, 30, -40]
+assert (_max_: nums) == 30
+assert (_max_.abs(): nums) == -40
```
## Operator Overloading
diff --git a/docs/optionals.md b/docs/optionals.md
index 131daf19..326d77b5 100644
--- a/docs/optionals.md
+++ b/docs/optionals.md
@@ -90,14 +90,11 @@ an `Abort` type like `fail()` or `exit()`:
```tomo
maybe_x : Int? = 5
->> maybe_x or -1
-= 5 : Int
->> maybe_x or fail("No value!")
-= 5 : Int
+assert (maybe_x or -1) == 5
+assert (maybe_x or fail("No value!")) == 5
maybe_x = none
->> maybe_x or -1
-= -1 : Int
+assert (maybe_x or -1) == -1
>> maybe_x or fail("No value!")
# Failure!
diff --git a/docs/paths.md b/docs/paths.md
index 2fa55b13..810cb6df 100644
--- a/docs/paths.md
+++ b/docs/paths.md
@@ -13,10 +13,8 @@ syntax. A path literal begins with either `(/`, `(./`, `(../`, or `(~/` and cont
until a matching closing parenethesis:
```tomo
->> (/tmp)
-= (/tmp)
->> (~/path with/(parens) is/ok/)
-= (~/path with/(parens) is/ok/)
+assert (/tmp) == (/tmp)
+assert (~/path with/(parens) is/ok/) == (~/path with/(parens) is/ok/)
```
### Interpolation
diff --git a/docs/pointers.md b/docs/pointers.md
index 254db07e..fb7668f1 100644
--- a/docs/pointers.md
+++ b/docs/pointers.md
@@ -18,16 +18,14 @@ func no_mutation_possible(nums:[Int])
...
my_nums := [0, 1, 2]
no_mutation_possible(my_nums)
->> my_nums
-= [0, 1, 2]
+assert my_nums == [0, 1, 2]
func do_mutation(nums:@[Int])
nums[1] = 10 // The mutates the value at the given pointer's location
...
my_nums := @[0, 1, 2]
do_mutation(my_nums)
->> my_nums
-= @[10, 1, 2]
+assert my_nums == @[10, 1, 2]
```
## Dereferencing
@@ -37,8 +35,7 @@ memory location using the `[]` postfix operator (with no value inside).
```tomo
nums := @[10, 20]
->> nums[]
-= [10, 20]
+assert nums[] == [10, 20]
```
## Equality and Comparisons
@@ -52,12 +49,10 @@ doing a mutation to the other.
```tomo
x := @[10, 20, 30]
y := @[10, 20, 30]
->> x == y
-= no
+assert x != y
z := x
->> x == z
-= yes
+assert x == z
```
Pointers are ordered by memory address, which is somewhat arbitrary, but
@@ -112,8 +107,7 @@ inside of any datastructures as elements or members.
```tomo
nums := @[10, 20, 30]
->> nums.first(func(x:&Int): x / 2 == 10)
-= 2 : Int?
+assert nums.first(func(x:&Int): x / 2 == 10) == 2
```
Normal `@` pointers can be promoted to immutable view pointers automatically,
diff --git a/docs/reductions.md b/docs/reductions.md
index abd612b0..929e5d00 100644
--- a/docs/reductions.md
+++ b/docs/reductions.md
@@ -7,8 +7,7 @@ infix operator followed by a colon, followed by a collection:
```tomo
nums := [10, 20, 30]
sum := (+: nums)
->> sum
-= 60 : Int?
+assert sum == 60
```
Reductions return an optional value which will be a null value if the thing
@@ -21,15 +20,12 @@ provide a fallback value:
nums : [Int] = []
sum := (+: nums)
->> sum
-= none : Int?
+assert sum == none
->> sum or 0
-= 0
+assert sum or 0 == 0
->> nums = [10, 20]
->> (+: nums)!
-= 30
+nums = [10, 20]
+assert (+: nums)! == 30
```
Reductions can be used as an alternative to generic functions like `sum()`,
@@ -38,20 +34,16 @@ Reductions can be used as an alternative to generic functions like `sum()`,
```tomo
# Sum:
->> (+: [10, 20, 30])!
-= 60
+assert (+: [10, 20, 30])! == 60
# Product:
->> (*: [2, 3, 4])!
-= 24
+assert (*: [2, 3, 4])! == 24
# Any:
->> (or: [no, yes, no])!
-= yes
+assert (or: [no, yes, no])! == yes
# All:
->> (and: [no, yes, no])!
-= no
+assert (and: [no, yes, no])! == no
```
## Minimum and Maximum
@@ -61,12 +53,10 @@ a collection using the `_min_` and `_max_` infix operators.
```tomo
# Get the maximum value:
->> (_max_: [10, 30, 20])!
-= 30
+assert (_max_: [10, 30, 20])! == 30
# Get the minimum value:
->> (_min_: [10, 30, 20])!
-= 10
+assert (_min_: [10, 30, 20])! == 10
```
Reducers also support field and method call suffixes, which makes it very easy
@@ -76,28 +66,22 @@ feature_.
```tomo
# Get the longest text:
->> (_max_.length: ["z", "aaaaa", "mmm"])!
-= "aaaaa"
+assert (_max_.length: ["z", "aaaaa", "mmm"])! == "aaaaa"
# Get the number with the biggest absolute value:
->> (_max_.abs(): [1, -2, 3, -4])!
-= -4
+assert (_max_.abs(): [1, -2, 3, -4])! == -4
```
You can also use suffixes on other operators:
```tomo
texts := ["x", "y", "z"]
->> (==: texts)
-= no
->> (==.length: texts)
-= yes
->> (+.length: texts)
-= 3
+assert (==: texts) == no
+assert (==.length: texts) == yes
+assert (+.length: texts) == 3
nums := [1, 2, -3]
->> (+.abs(): nums)
-= 6
+assert (+.abs(): nums) == 6
```
## Comprehensions
@@ -108,10 +92,8 @@ while filtering out values or while applying a transformation:
```tomo
# Sum the lengths of these texts:
->> (+: t.length for t in ["a", "bc", "def"])!
-= 6
+assert (+: t.length for t in ["a", "bc", "def"])! == 6
# Sum the primes between 1-100:
->> (+: i for i in 100 if i.is_prime())!
-= 1060
+assert (+: i for i in 100 if i.is_prime())! == 1060
```
diff --git a/docs/serialization.md b/docs/serialization.md
index 287cbda7..8c72cb83 100644
--- a/docs/serialization.md
+++ b/docs/serialization.md
@@ -67,12 +67,14 @@ struct Cycle(name:Text, next:@Cycle?=none)
c := @Cycle("A")
c.next = @Cycle("B", next=c)
->> c
+say("$c")
+# @Cycle(name="A", next=@Cycle(name="B", next=@~1))
+bytes : [Byte] = c
+say("$bytes")
+# [0x02, 0x02, 0x41, 0x01, 0x04, 0x02, 0x42, 0x01, 0x02]
+roundtrip : @Cycle = bytes
+say("$roundtrip")
# @Cycle(name="A", next=@Cycle(name="B", next=@~1))
->> bytes : [Byte] = c
-# [0x02, 0x02, 0x41, 0x01, 0x04, 0x02, 0x42, 0x01, 0x02] : [Byte]
->> roundtrip : @Cycle = bytes
-# @Cycle(name="A", next=@Cycle(name="B", next=@~1)) : @Cycle
assert roundtrip.next.next == roundtrip
```
diff --git a/docs/structs.md b/docs/structs.md
index 1dfa49c9..8f280fab 100644
--- a/docs/structs.md
+++ b/docs/structs.md
@@ -6,10 +6,9 @@ types that can be accessed by fields:
```tomo
struct Foo(name:Text, age:Int)
...
->> my_foo := Foo("Bob", age=10)
-= Foo(name="Bob", age=10)
->> my_foo.name
-= "Bob"
+my_foo := Foo("Bob", age=10)
+assert my_foo == Foo(name="Bob", age=10)
+assert my_foo.name == "Bob"
```
Structs are value types and comparisons on them operate on the member values
@@ -49,11 +48,8 @@ struct Password(raw_password_text:Text; secret)
struct User(username:Text, password:Password)
...
user := User("Stanley", Password("Swordfish"))
->> user
-= User(username="Stanley", password=Password(...))
-
->> "$user" == 'User(username="Stanley", password=Password(...))'
-= yes
+assert user == User("Stanley", Password("Swordfish"))
+assert "You are: $user" == 'You are: User(username="Stanley", password=Password(...))'
```
Designing APIs so they take secrecy-protected structs instead of raw data
@@ -62,17 +58,12 @@ your logs! Secrecy-protected values still work the same as any other struct,
they just don't divulge their contents when converting to strings:
```tomo
->> user.password == Password("Swordfish")
-= yes
+assert user.password == Password("Swordfish")
```
You can also access the fields directly, but hopefully this extra amount of
friction reduces the chances of accidentally divulging sensitive content:
```tomo
->> user.password
-= Password(...)
-
->> user.password.raw_password_text
-= "Swordfish"
+assert user.password.raw_password_text == "Swordfish"
```
diff --git a/docs/tables.md b/docs/tables.md
index eaf0083e..00e3e8c0 100644
--- a/docs/tables.md
+++ b/docs/tables.md
@@ -40,10 +40,8 @@ optional value:
```tomo
table := {"A": 1, "B": 2}
->> table["A"]
-= 1?
->> table["missing"]
-= none
+assert table["A"] == 1
+assert table["missing"] == none
```
As with all optional values, you can use the `!` postfix operator to assert
@@ -51,11 +49,9 @@ that the value is non-none (and create a runtime error if it is), or you can
use the `or` operator to provide a fallback value in the case that it's none:
```tomo
->> table["A"]!
-= 1
+assert table["A"]! == 1
->> table["missing"] or -1
-= -1
+assert (table["missing"] or -1) == -1
```
### Fallback Tables
@@ -66,18 +62,15 @@ is not found in the table itself:
```tomo
t := {"A": 10}
t2 := {"B": 20; fallback=t}
->> t2["A"]
-= 10?
+assert t2["A"] == 10
```
The fallback is available by the `.fallback` field, which returns an optional
table value:
```tomo
->> t2.fallback
-= {"A": 10}?
->> t.fallback
-= none
+assert t2.fallback == {"A": 10}
+assert t.fallback == none
```
### Default Values
@@ -87,13 +80,10 @@ present in the table or its fallback (if any).
```tomo
counts := &{"foo": 12; default=0}
->> counts["foo"]
-= 12
->> counts["baz"]
-= 0
+assert counts["foo"] == 12
+assert counts["baz"] == 0
counts["baz"] += 1
->> counts["baz"]
-= 1
+assert counts["baz"] == 1
```
When values are accessed from a table with a default value, the return type
@@ -108,8 +98,7 @@ You can assign a new key/value mapping or overwrite an existing one using
t := {"A": 1, "B": 2}
t["B"] = 222
t["C"] = 333
->> t
-= {"A": 1, "B": 222, "C": 333}
+assert t == {"A": 1, "B": 222, "C": 333}
```
## Length
@@ -117,8 +106,7 @@ t["C"] = 333
Table length can be accessed by the `.length` field:
```tomo
->> {"A": 10, "B": 20}.length
-= 2
+assert {"A": 10, "B": 20}.length == 2
```
## Accessing Keys and Values
@@ -128,10 +116,8 @@ constant-time immutable slice of the internal data from the table:
```tomo
t := {"A": 10, "B": 20}
->> t.keys
-= ["A", "B"]
->> t.values
-= [10, 20]
+assert t.keys == ["A", "B"]
+assert t.values == [10, 20]
```
## Iteration
diff --git a/docs/text.md b/docs/text.md
index 2df27811..b41140f7 100644
--- a/docs/text.md
+++ b/docs/text.md
@@ -247,10 +247,8 @@ when you think of "letters" in a string. If you have text with an emoji that has
several joining modifiers attached to it, that text has a length of 1.
```tomo
->> "hello".length
-= 5
->> "👩🏽‍🚀".length
-= 1
+assert "hello".length == 5
+assert "👩🏽‍🚀".length == 1
```
### Iteration