Repository navigation
Adds host function providers for guards - #457
Merged
Merged
Conversation
A host registers named functions a chart may call from cond and expr
with :function_providers, a list of Predicator.FunctionProvider
modules, on Statifier.compile/2, MachineState.new/2 and
Session.start_link/2.
- Evaluator.context/1 swaps the stamped map into the constant
context's functions field; nil keeps the constant unchanged.
- MachineState gains a functions field; Position drops it and
put_function_providers/2 re-stamps it; a resumed session, a
recording and Replay all carry the list.
- compile/2 refuses a call to a name that is not In, a builtin or a
declared function ({:unknown_function, name, source}); the
declared names persist as :function_names in compile_opts.
- A provider may not shadow In or a builtin.
ADR-0078 records the decision as proposed; docs/extending.md gains
the guide section. No conformance result moves.
Refs: st-g648
- FunctionCalls reads only top-level call instructions, so a literal list shaped like a call is data, not a call. - Chart.from_binary refuses a blob whose options compile/2 would raise on (:function_providers, or :function_names not a list of strings) as :not_a_statifier_blob, so it never raises or calls a module. - Restores Compiler.Error's moduledoc indentation. - Tests pin every expression site the check reads, error order, dedupe and location, option shape errors, two providers' order through the recording codec, from_binary/import blanking functions, an unknown provider module in a recording, and no key when empty. Refs: st-g648
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
A host building rich charts wants pure named guards it can call from
condandexpr- a blast radius over a list of paths, business hours at a timestamp in a zone, whether two intervals overlap - instead of precomputing every such fact into the event payload. predicator already supports this throughPredicator.FunctionProvider. statifier did not expose it: every evaluation context starts from one compile-time constant holdingIn/1and the builtins, and no option added to it.What
:function_providers, a list ofPredicator.FunctionProvidermodules, accepted byStatifier.compile/2,Statifier.MachineState.new/2andStatifier.Session.start_link/2.nilor[]keeps today's behaviour.Statifier.Evaluator.Functions.from_providers/1resolves the list once into the full dispatch map, and it is stamped onto a newMachineStatefunctionsfield.Statifier.Evaluator.context/1swaps that map into the constant context with one field write. With the fieldnil, the constant is used unchanged. A provider may not shadowInor a builtin.Statifier.Positiondrops the field likeinvoke_typesandsend_types, andMachineState.put_function_providers/2re-stamps it. A session started with:resumere-stamps the list itself. A recording keeps the list as module name strings, andStatifier.Replaystamps it on both of its start branches.Statifier.Compiler.FunctionCalls.check/2refuses any call whose name is notIn, a builtin or a declared function, with{:unknown_function, name, source}at the element that holds the call. The declared names persist as:function_namesincompile_opts, so a chart blob recompiles without the provider modules.:function_namesis also accepted as an input.Session.start_link/2refuses a list that does not resolve before it spawns anything:{:error, {:function_providers, reason}}.docs/extending.mdgains "Host functions forcondandexpr", which states the purity contract: a function reads only its arguments and the host term, does no I/O and reads no clock, so a replay or a resume evaluates it the same.Notes
Review round: wurk-diff-critic and wurk-test-critic, one fresh round over origin/main...9400c10, returned 12 findings (4 diff, 8 test; none must-fix). 11 fixed in e595eeb: the call check reads only top-level call instructions, so a literal list shaped like a call is no longer refused;
Statifier.Chart.from_binary/1answers:not_a_statifier_blobfor a blob whose optionscompile/2would raise on instead of raising; theStatifier.Compiler.Errormoduledoc indentation is restored; and new tests pin every expression site, error order, dedupe and location, option shape errors, two providers through the recording codec,from_binary/2andimport/2blanking the functions, an unknown provider module in a recording, and no key when the list is empty. 1 declined: a chart given as<invoke>content cannot call a host function, because children do not inherit the list; this is documented in the guide and ADR-0078 and left for a later change. An advisory that the builtinsMath.randomandDate.noware themselves impure is outside this change and was not acted on. Head reviewed: 9400c10; head after fixes: e595eeb.error.executionat every evaluation of that call. It now fails to compile. The check stays at compile time as built, ruled by the operator, 2026-10-12. The changelog fragment lists it under Changed.<invoke>does not inherit the list. That is left for a later change.f()as a non-assignable namelist entry. They now usetrim(copies), a builtin call with the same intent. TheMachineStatefield-set test counts the new field.mix quality --profile mergepassed, ADR judge included. On e595eeb, the full gate is green and attested. A full conformance run fails the same three optional cases asmain, and the regression ratchet passes.Closes st-g648