Paths, .field, hoisting, and (std hashset)
- (.field value), and get, try-get, set and update for reading and copying along a path of fields and keys, with ref-set in the generic operations. - please-hoist-i-promise-i-am-not-naughty, in the macros section. - (std set) is (std hashset), and the collector is hash-setting. 🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-5) Co-Authored-By: eca-agent <git@eca.dev>
This commit is contained in:
parent
1fe22a4dfd
commit
48771a225a
3 changed files with 85 additions and 11 deletions
|
|
@ -559,6 +559,52 @@ Records are updated by copying, with @code{record-set}:
|
|||
(record-ref ada tricks) ;; still 0
|
||||
}|
|
||||
|
||||
A field can also be read with @code{(.field value)}, which is the same as @code{record-ref}. On its own, @code{.field} is a function, so it can be passed around:
|
||||
|
||||
@codeblock[#:lang "bjolang"]|{
|
||||
(.tricks ada) ;; 0
|
||||
(list-map .tricks (list ada (won-trick ada))) ;; '(0 1)
|
||||
}|
|
||||
|
||||
@subsubsection{Reading and changing nested data}
|
||||
|
||||
Data is often a record holding a map holding a vec of records. Reading something deep inside it, or making a copy with one thing changed deep inside it, is a lot of @code{record-ref} and @code{record-set} by hand. The prelude has four macros for it instead. Each takes a value and a @em{path}: a list of steps, where a step written @code{.field} is a field, and any other step is a key into a map or an index into a vec.
|
||||
|
||||
@codeblock[#:lang "bjolang"]|{
|
||||
(type
|
||||
(: Player (Record (: name string) (: score int)))
|
||||
(: Team (Record (: players (Vec Player)) (: tags (Map string int)))))
|
||||
|
||||
(def team (Team (players [(Player (name "Ada") (score 3))
|
||||
(Player (name "Bo") (score 5))])
|
||||
(tags #map(("fruit" 1)))))
|
||||
|
||||
(get team .players 1 .name) ;; "Bo"
|
||||
(try-get team .tags "veg") ;; None
|
||||
(set team .players 0 .name "Bea") ;; a new Team; team is unchanged
|
||||
(update team .players 1 .score (fun (s) (+ s 10))) ;; Bo has 15 in the new one
|
||||
}|
|
||||
|
||||
@read-table{
|
||||
| Form | Does |
|
||||
|---------------------------------------+------------------------------------------------------------------------------|
|
||||
| @code{(get value step ...)} | reads along the path, and throws if a key is missing |
|
||||
| @code{(try-get value step ...)} | the same as an @code{Option}: @code{None} at the first key that is missing |
|
||||
| @code{(set value step ... new)} | a copy of @code{value} with @code{new} at the end of the path |
|
||||
| @code{(update value step ... f)} | a copy of @code{value} with @code{(f old)} at the end of the path |
|
||||
}
|
||||
|
||||
A key is read with @code{ref} and written with @code{ref-set}, so the path can go through anything that implements the traits behind them: @code{Refable} to read, and @code{Settable} to write, which @code{Vec} and @code{Map} do. A field step can read a .NET property too, as in @code{(get team .players 0 .name .-Length)}, but only a Bjolang record or struct can be copied with a field changed, so @code{set} and @code{update} refuse one.
|
||||
|
||||
Some details worth knowing:
|
||||
|
||||
@itemlist{
|
||||
@item{@code{set} does not read the last key, so setting a key a map does not have adds it. A key that is missing earlier in the path throws, as @code{ref} does.}
|
||||
@item{@code{try-get} gives @code{None} for a missing map key, but a vec index out of range is still an error, as it is for @code{try-ref}. The keys after a missing one are not evaluated.}
|
||||
@item{@code{set} and @code{update} evaluate the value, then each key from left to right, then the new value or the function, each of them exactly once.}
|
||||
@item{These are macros, so a step is a field or a key by how it is written, not by its type: @code{.name} is always a field, and anything else is always a key.}
|
||||
}
|
||||
|
||||
@subsection{Pattern matching}
|
||||
|
||||
Pattern matching is how you take values apart in bjolang. There is no @code{car}/@code{cdr} style of programming on unions: you say what shape you expect and the compiler binds the parts for you, and checks that you did not forget a shape.
|
||||
|
|
@ -1043,7 +1089,7 @@ Bjolang's collections are immutable by default. Adding to a vec or setting a key
|
|||
| @code{(Vec %a)} | @code{[1 2 3]} | an indexable vector (an RRB tree), cheap everywhere |
|
||||
| @code{(Array %a)} | @code{#[1 2 3]} | a mutable .NET array of fixed length |
|
||||
| @code{(Map %k %v)} | @code{#map(("a" 1) ("b" 2))} | a hash map (a CHAMP trie) |
|
||||
| @code{(Set %a)} | | a hash set, in @code{(std set)} |
|
||||
| @code{(HashSet %a)} | | a hash set, in @code{(std hashset)} |
|
||||
| @code{(OrderedMap %k %v)} | | a sorted map (a B-tree), in @code{(std orderedmap)} |
|
||||
| @code{(OrderedSet %a)} | | a sorted set, in @code{(std orderedset)} |
|
||||
}
|
||||
|
|
@ -1146,13 +1192,13 @@ When a map is walked as a collection, for example in a @code{loop} or with @code
|
|||
Sets and the sorted collections are libraries, so they have to be imported. Their functions are named like the map functions, with their own prefix:
|
||||
|
||||
@codeblock[#:lang "bjolang"]|{
|
||||
(import (std set))
|
||||
(import (std hashset))
|
||||
(import (std orderedmap))
|
||||
|
||||
(def s (list->set (list 3 1 4 1 5 9 2 6 5)))
|
||||
(set-length s) ;; 7
|
||||
(set-contains? s 4) ;; #t
|
||||
(set-contains? (set-add s 7) 7) ;; #t
|
||||
(def s (list->hashset (list 3 1 4 1 5 9 2 6 5)))
|
||||
(hashset-length s) ;; 7
|
||||
(hashset-contains? s 4) ;; #t
|
||||
(hashset-contains? (hashset-add s 7) 7) ;; #t
|
||||
|
||||
(def prices (orderedmap-set
|
||||
(orderedmap-set
|
||||
|
|
@ -1202,6 +1248,7 @@ Many operations are traits, so they work on every collection that implements the
|
|||
| @code{(length c)} @code{(empty? c)} | lists, vecs, arrays, maps, strings | size, and whether it is empty |
|
||||
| @code{(ref c key)} | vecs, arrays, maps, strings | the element at an index or key; throws |
|
||||
| @code{(try-ref c key)} | the same | the element as an @code{Option} |
|
||||
| @code{(ref-set c key x)} | vecs, maps | a copy with @code{x} at the key |
|
||||
| @code{(add c x)} @code{(add-all c xs)} | vecs, maps, sets | a new collection with more in it |
|
||||
| @code{(add! b x)} @code{(add-all! b xs)} | builders and transients | put things in, in place |
|
||||
| @code{(fold f init c)} | anything you can loop over | a left fold |
|
||||
|
|
@ -1214,7 +1261,7 @@ Many operations are traits, so they work on every collection that implements the
|
|||
(ref [10 20 30] 1) ;; 20
|
||||
(try-ref #map((:a 1)) :b) ;; None
|
||||
(add #map(("a" 1)) ("b" . 2)) ;; for a map, the element is a pair
|
||||
(add-all (set-empty) [1 1 2 2 3]) ;; a set of three
|
||||
(add-all (hashset-empty) [1 1 2 2 3]) ;; a set of three
|
||||
(fold + 0 [1 2 3 4]) ;; 10
|
||||
(find #(> & 3) (list 1 5 7)) ;; (Some 5)
|
||||
|
||||
|
|
@ -1347,7 +1394,7 @@ An accumulator names a collector and what to feed it:
|
|||
| @code{(listing x)} | a @code{List}, in order |
|
||||
| @code{(vecing x)} | a @code{Vec} |
|
||||
| @code{(mapping (k . v))} | a @code{Map}, from pairs |
|
||||
| @code{(setting x)} | a @code{Set}, from @code{(std set)} |
|
||||
| @code{(hash-setting x)} | a @code{HashSet}, from @code{(std hashset)} |
|
||||
| @code{(stringing c)} | a @code{string}, from characters |
|
||||
| @code{(summing x)} | the sum of @code{x} |
|
||||
| @code{(counting x)} | how many rounds reached it |
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue