From d804b09b02b9c4a6ea6b16ae85524a704796cbc1 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sun, 18 Aug 2024 20:28:16 -0400 Subject: Added a .length field to arrays/sets/tables, added a .max_size field to channels, and updated the API --- api/arrays.md | 9 +++++ api/channels.md | 6 ++-- api/pointers.md | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ api/sets.md | 9 +++++ api/tables.md | 12 +++++++ 5 files changed, 137 insertions(+), 3 deletions(-) create mode 100644 api/pointers.md (limited to 'api') diff --git a/api/arrays.md b/api/arrays.md index 34da6e8f..0dede5dd 100644 --- a/api/arrays.md +++ b/api/arrays.md @@ -42,6 +42,15 @@ Comprehensions can be combined with regular items or other comprehensions: = [-1, 30, 40, 50, 60, 70, 80, 1, 2, 3] ``` +## Length + +Array length can be accessed by the `.length` field: + +```tomo +>> [10, 20, 30].length += 3 +``` + ## Indexing Array values are accessed using square bracket indexing. Since arrays are diff --git a/api/channels.md b/api/channels.md index 4b8d416a..48116dc5 100644 --- a/api/channels.md +++ b/api/channels.md @@ -5,13 +5,13 @@ although they can also be used as a general-purpose queue. ## Syntax -The syntax to create a channel is `|T|`, where `T` is the type that will be +The syntax to create a channel is `|:T|`, where `T` is the type that will be passed through the channel. You can also specify a maximum size for the channel, which will cause pushing to block until the recipient has popped from the channel if the maximum size is reached. ```tomo -channel := |Int| +channel := |:Int| channel:push(10) channel:push(20) >> channel:pop() @@ -19,7 +19,7 @@ channel:push(20) >> channel:pop() = 20 -small_channel := |Int; max_size=5| +small_channel := |:Int; max_size=5| ``` ## Channel Methods diff --git a/api/pointers.md b/api/pointers.md new file mode 100644 index 00000000..67043084 --- /dev/null +++ b/api/pointers.md @@ -0,0 +1,104 @@ +# Pointers + +Pointers are numeric values that represent a location in memory where some type +of data lives. Pointers are created using either the `@` prefix operator to +**a**llocate heap memory or the `&` prefix operator to get the address of a +variable. Stack pointers (`&`) are more limited than heap pointers (`@`) and +cannot be stored inside an array, set, table, struct, enum, or channel. +However, stack pointers are useful for methods that mutate local variables and +don't need to save the pointer anywhere. + +Pointers are the way in Tomo that you can create mutable data. All +datastructures are by default, immutable, but using pointers, you can create +a region of memory where different immutable values can be held, which change +over time. + +```tomo +func no_mutation_possible(nums:[Int]): + nums[1] = 10 // Type error! + +func do_mutation(nums:@[Int]): + nums[1] = 10 // okay + +... + +my_nums := @[0, 1, 2] +do_mutation(my_nums) +``` + +In general, heap pointers can be used as stack pointers if necessary, since +the usage of stack pointers is restricted, but heap pointers don't have the +same restrictions, so it's good practice to define functions that don't need +to store pointers to use stack references. This lets you pass references to +local variables or pointers to heap data depending on your needs. + +```tomo +func swap_first_two(data:&[Int]): + data[1], data[2] = data[2], data[1] + +... + +heap_nums := @[10, 20, 30] +swap_first_two(heap_nums) + +local_nums := [10, 20, 30] +swap_first_two(&local_nums) +``` + +## Dereferencing + +Pointers can be dereferenced to access the value that's stored at the pointer's +memory location using the `[]` postfix operator (with no value inside). + +```tomo +nums := @[10, 20] +>> nums[] += [10, 20] +``` + +## Equality and Comparisons + +When comparing two pointers, the comparison operates on the _memory address_, +not the contents of the memory. This is "referential" equality, not +"structural" equality. The easy way to think about it is that two pointers are +equal to each other only if doing a mutation to one of them is the same as +doing a mutation to the other. + +```tomo +x := @[10, 20, 30] +y := @[10, 20, 30] +>> x == y += no + +z := x +>> x == z += yes +``` + +Pointers are ordered by memory address, which is somewhat arbitrary, but +consistent. + +## Null Safety + +Tomo pointers are, by default, guaranteed to be non-null. If you write a +function that takes either a `&T` or `@T`, the value that will be given +is always non-null. However, optional pointers can be used by adding a +question mark to the type: `&T?` or `@T?`. A null value can be created +using the syntax `!@T` or `!&T`. You can also append a question mark to +a pointer value so the type checker knows it's supposed to be optional: + +``` +optional := @[10, 20]? +optional := &foo? +``` + +The compiler will not allow you to dereference an optionally null pointer +without explicitly checking for null. To do so, use pattern matching like +this: + +``` +when optional is @ptr: + ok := ptr[] +else: + say("Oh, it was null") +``` diff --git a/api/sets.md b/api/sets.md index 6dcc9ca9..850d1443 100644 --- a/api/sets.md +++ b/api/sets.md @@ -48,6 +48,15 @@ set := {10, 20, 30} = [10, 20, 30] ``` +## Length + +Set length can be accessed by the `.length` field: + +```tomo +>> {10, 20, 30}.length += 3 +``` + ## Iteration You can iterate over the items in a table like this: diff --git a/api/tables.md b/api/tables.md index f132d70a..f4753efb 100644 --- a/api/tables.md +++ b/api/tables.md @@ -50,6 +50,18 @@ t2 := {"B": 20; fallback=t} = 10 ``` +The fallback is available by the `.fallback` field, which returns an optional +readonly pointer to the fallback table (if present) or null if it is not. + +## Length + +Table length can be accessed by the `.length` field: + +```tomo +>> {"A":10, "B":20}.length += 2 +``` + ## Accessing Keys and Values The keys and values of a table can be efficiently accessed as arrays using a -- cgit v1.2.3