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
|
|
@ -97,7 +97,7 @@ A list-shaped module path such as @code{(std random)} refers to a standard libra
|
|||
| Module | What it has |
|
||||
|---------------------------------+------------------------------------------------------|
|
||||
| @code{(std random)} | random numbers, @code{shuffle-vec} |
|
||||
| @code{(std set)} | sets; @code{(std orderedset)}, @code{(std orderedmap)} |
|
||||
| @code{(std hashset)} | sets; @code{(std orderedset)}, @code{(std orderedmap)} |
|
||||
| @code{(std ports)} | reading a whole port: @code{port->lines} and friends |
|
||||
| @code{(std fmt)} | text layout |
|
||||
| @code{(std rx)} | regular expressions |
|
||||
|
|
@ -178,7 +178,7 @@ Imports can be wrapped in modifiers to filter which definitions are brought into
|
|||
0)
|
||||
}|
|
||||
|
||||
Modifiers can be nested and evaluate from the inside out. For example, @code{(prefix (except (std set) set-map) "s/")} excludes @code{set-map} and prefixes all remaining names with @code{s/}.
|
||||
Modifiers can be nested and evaluate from the inside out. For example, @code{(prefix (except (std hashset) hashset-map) "s/")} excludes @code{hashset-map} and prefixes all remaining names with @code{s/}.
|
||||
|
||||
Note that @code{only} and @code{except} apply exclusively to functions and macros, not types. A module's exported types are always imported, ensuring that signatures referencing those types remain valid and resolvable:
|
||||
|
||||
|
|
@ -1177,6 +1177,33 @@ carries a list, so this literal cannot be one. Its tags are play, say or pass.
|
|||
|
||||
This elaboration mechanism only applies in positions where the type checker already knows that a specific union type is expected (which is why @code{script} and @code{more} include explicit type signatures). Standard library modules like @code{(std run)} and @code{(std fmt)} use this exact technique for command pipelines and text layouts.
|
||||
|
||||
@subsubsection{Computing a value once}
|
||||
|
||||
A macro often builds a value out of constants, and that value is then built again every time the expansion is evaluated. When that is expensive, wrap it in @code{please-hoist-i-promise-i-am-not-naughty}: the form is evaluated the first time it is reached, and every later evaluation gets that same value. This is what @code{#rx(...)} does, so a regex is compiled once rather than once per match.
|
||||
|
||||
@codeblock[#:lang "bjolang"]|{
|
||||
;; (tag-set "a" "b" ...) is a HashSet of the strings, built once.
|
||||
(def/macro (tag-set form inject compare)
|
||||
(syntax-match form
|
||||
((_ tag ...)
|
||||
#'(please-hoist-i-promise-i-am-not-naughty
|
||||
(list->hashset (list ,@tag))))))
|
||||
}|
|
||||
|
||||
If computing the value throws, nothing is kept, and the next evaluation tries again. The same form written in two places is one value.
|
||||
|
||||
The long name is a promise about what the compiler cannot check: that the form has no side effects worth repeating, and that it does not depend on things that change from call to call, such as a parameter or an effect handler. Everything it can check, it does, and a form that breaks a rule is a compile error that lists every reason. The form cannot:
|
||||
|
||||
@itemlist{
|
||||
@item{refer to a local variable or a function parameter;}
|
||||
@item{read a @code{def} of the module it is in, or any @code{def/mutable};}
|
||||
@item{use a type variable of the function around it;}
|
||||
@item{call a bjoroutine, @code{yield} to a sequence, or leave a @code{with-return};}
|
||||
@item{be void, or produce something that can be changed in place, such as an array, a box or a mutable collection.}
|
||||
}
|
||||
|
||||
It may use literals, functions, and the defs of other modules. A macro that puts its caller's code inside the form passes these rules on to its caller, so keep the form to parts the macro built itself.
|
||||
|
||||
@subsubsection{Macro limitations}
|
||||
|
||||
When designing macros, keep the following constraints in mind:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue