Module: Rigor::Builtins::HktBuiltins
- Defined in:
- lib/rigor/builtins/hkt_builtins.rb
Overview
ADR-20 slices 2c + 3 — Rigor-bundled Lightweight HKT registrations that ship with every analyzer instance.
The set is intentionally small at v0.1.x: only the URIs whose payoff justifies hardcoded definitions. Plugin
authors register more URIs through their manifests; user .rbs overlays register through the
%a{rigor:v1:hkt_register} / %a{rigor:v1:hkt_define} annotations Slice 1 ships.
Today's contents:
-
json::value[K]— the recursive sum stdlib'sJSON.parsereturns. Body:nil | true | false | Integer | Float | String | Array[App[json::value, K]] | Hash[K, App[json::value, K]]The reducer handles the self-recursive
Appnodes via lazy "tying-the-knot" (see HktReducer).K = Stringmatches stdlib's default key handling;K = Symbolmatchessymbolize_names: true.
Constant Summary collapse
- METHOD_RETURN_OVERRIDES =
{ # JSON — stdlib's `json` library. Upstream rbs declares # `(string, ?options) -> untyped`; the HKT-builtin tier # tightens to the recursive `json::value[K]` union. # `load_file` / `load_file!` share the `?options` slot # so the `symbolize_names: true` discriminator applies # to them too (just like `parse` / `load`). ["JSON", :parse, :singleton] => JSON_VALUE_SPEC, ["JSON", :parse!, :singleton] => JSON_VALUE_SPEC, ["JSON", :load, :singleton] => JSON_VALUE_SPEC, ["JSON", :load_file, :singleton] => JSON_VALUE_SPEC, ["JSON", :load_file!, :singleton] => JSON_VALUE_SPEC, # YAML.safe_load / Psych.safe_load — default # `permitted_classes: []` admits exactly the JSON # vocabulary (nil / true / false / Integer / Float / # String / Array / Hash), so the json::value tree # also describes them. When the call passes a literal # `permitted_classes: [Date, Symbol, ...]` Array, the # `:yaml_permitted_classes` post_reduce unions each # named class into the result. Non-literal options # (a variable, a constant reference, a `+ classes` # concat) silently no-op and the caller observes the # base json::value envelope only. YAML.load / # YAML.unsafe_load deliberately stay out of the # override table — they can return ANY Ruby object # and have no useful HKT envelope. ["YAML", :safe_load, :singleton] => YAML_SAFE_VALUE_SPEC, ["YAML", :safe_load_file, :singleton] => YAML_SAFE_VALUE_SPEC, ["Psych", :safe_load, :singleton] => YAML_SAFE_VALUE_SPEC, ["Psych", :safe_load_file, :singleton] => YAML_SAFE_VALUE_SPEC, # CSV.parse / CSV.read — no-headers shape only. # Upstream rbs declares broader return shapes but # the common case is `Array[Array[String?]]` which # the `csv::parsed[String]` URI matches. The # `headers: true` shape (`CSV::Table` of `CSV::Row`) # is NOT covered — calls passing the option fall # through to the upstream RBS type. CSV.foreach also # falls through (it yields rows rather than # returning a typed structure). ["CSV", :parse, :singleton] => CSV_PARSED_SPEC, ["CSV", :read, :singleton] => CSV_PARSED_SPEC }.freeze
Class Method Summary collapse
-
.apply_post_reduce(kind, reduced, arg_types) ⇒ Object
Slice 2c-bis — post-reduce hook.
-
.augment_with_yaml_permitted_classes(reduced, arg_types) ⇒ Object
Inspects arg_types for a
permitted_classes: [<Class>, ...]literal Array in the options Hash and unions each named class into the reduced result. - .csv_parsed_body_tree ⇒ Object
- .csv_parsed_definition ⇒ Object
- .csv_parsed_registration ⇒ Object
-
.discriminated_args(spec, arg_types) ⇒ Object
Per-spec discriminator dispatch.
-
.json_symbolize_names?(arg_types) ⇒ Boolean
Returns true iff the call-site's 2nd argument is a
Type::HashShapecarrying a literalsymbolize_names: trueentry. - .json_value_body_tree ⇒ Object
- .json_value_definition ⇒ Object
- .json_value_registration ⇒ Object
-
.method_return_override(class_name:, method_name:, kind:, arg_types: nil, hkt_registry: nil) ⇒ Rigor::Type?
The reduced HKT type for the given (class_name, method_name, kind) triple, or
nilwhen no built-in override is registered. -
.permitted_class_nominals(value) ⇒ Object
Extract Singleton-class elements from a Tuple or Array-shape carrier, mapping each to its Nominal counterpart.
-
.registry ⇒ Rigor::Inference::HktRegistry
Frozen registry pre-seeded with all bundled HKT registrations + bodies.
Class Method Details
.apply_post_reduce(kind, reduced, arg_types) ⇒ Object
Slice 2c-bis — post-reduce hook. Receives the already-
reduced Type and the call-site's arg_types; returns
a (possibly augmented) Type. kind = nil is the
identity (passes the reduced type through unchanged).
Only :yaml_permitted_classes is implemented today;
plugin / Rigor-bundled callers wanting their own
post-reduce hooks add a branch here.
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 |
# File 'lib/rigor/builtins/hkt_builtins.rb', line 271 def apply_post_reduce(kind, reduced, arg_types) case kind when :yaml_permitted_classes augment_with_yaml_permitted_classes(reduced, arg_types) else # `nil` (no post-reduce declared) and any future # unrecognised kind both pass the reduced type # through unchanged. Unknown kinds are silently # tolerated rather than raised because adding a # new kind on a Rigor upgrade should not crash a # stale METHOD_RETURN_OVERRIDES entry on the # caller side. reduced end end |
.augment_with_yaml_permitted_classes(reduced, arg_types) ⇒ Object
Inspects arg_types for a permitted_classes: [<Class>, ...] literal Array in the options Hash and unions
each named class into the reduced result. Non-literal
permitted_classes: values (a variable, a constant
reference, a concat) silently no-op and the caller
observes the base json::value envelope only. Defensive
against the various ways Ruby literal arrays surface
as Rigor types: Tuple[Singleton<Date>] for a single
element, Tuple[Singleton<Date>, Singleton<Symbol>]
for multiple, Nominal[Array, [Singleton<...>]] if
the analyzer widened (rare for literal arrays).
298 299 300 301 302 303 304 305 306 307 308 309 310 311 |
# File 'lib/rigor/builtins/hkt_builtins.rb', line 298 def augment_with_yaml_permitted_classes(reduced, arg_types) return reduced unless arg_types.is_a?(Array) && arg_types.size >= 2 opts = arg_types[1] return reduced unless opts.is_a?(Rigor::Type::HashShape) value = opts.pairs[:permitted_classes] || opts.pairs["permitted_classes"] return reduced if value.nil? extras = permitted_class_nominals(value) return reduced if extras.empty? Rigor::Type::Combinator.union(reduced, *extras) end |
.csv_parsed_body_tree ⇒ Object
51 52 53 |
# File 'lib/rigor/builtins/hkt_builtins.rb', line 51 def csv_parsed_body_tree Rigor::Inference::HktBodyParser.parse(CSV_PARSED_BODY, params: [:K]) end |
.csv_parsed_definition ⇒ Object
83 84 85 86 87 88 89 90 91 |
# File 'lib/rigor/builtins/hkt_builtins.rb', line 83 def csv_parsed_definition Rigor::Inference::HktRegistry.definition_with_body_tree( uri: :"csv::parsed", params: [:K], body_tree: csv_parsed_body_tree, source_path: __FILE__, source_line: __LINE__ - 5 ) end |
.csv_parsed_registration ⇒ Object
74 75 76 77 78 79 80 81 |
# File 'lib/rigor/builtins/hkt_builtins.rb', line 74 def csv_parsed_registration Rigor::Inference::HktRegistry::Registration.new( uri: :"csv::parsed", arity: 1, variance: [:out], bound: Rigor::Type::Combinator.untyped ) end |
.discriminated_args(spec, arg_types) ⇒ Object
Per-spec discriminator dispatch. Slice 3 ships one
built-in discriminator (json_symbolize_names) that
observes the optional 2nd argument's HashShape for a
literal symbolize_names: true entry. Plugin / Rigor-
bundled callers wanting their own discriminators add a
branch here.
239 240 241 242 243 244 245 246 |
# File 'lib/rigor/builtins/hkt_builtins.rb', line 239 def discriminated_args(spec, arg_types) default_args = spec[:args].map { |n| Rigor::Type::Nominal.new(n) } return default_args if arg_types.nil? return default_args unless spec[:discriminator] == :json_symbolize_names return default_args unless json_symbolize_names?(arg_types) [Rigor::Type::Nominal.new("Symbol")] end |
.json_symbolize_names?(arg_types) ⇒ Boolean
Returns true iff the call-site's 2nd argument is a
Type::HashShape carrying a literal
symbolize_names: true entry. Anything else
(no second arg, non-HashShape, missing key, non-literal
true) returns false so the default K = String
branch wins.
254 255 256 257 258 259 260 261 262 |
# File 'lib/rigor/builtins/hkt_builtins.rb', line 254 def json_symbolize_names?(arg_types) return false unless arg_types.is_a?(Array) && arg_types.size >= 2 opts = arg_types[1] return false unless opts.is_a?(Rigor::Type::HashShape) value = opts.pairs[:symbolize_names] || opts.pairs["symbolize_names"] value.is_a?(Rigor::Type::Constant) && value.value == true end |
.json_value_body_tree ⇒ Object
36 37 38 |
# File 'lib/rigor/builtins/hkt_builtins.rb', line 36 def json_value_body_tree Rigor::Inference::HktBodyParser.parse(JSON_VALUE_BODY, params: [:K]) end |
.json_value_definition ⇒ Object
64 65 66 67 68 69 70 71 72 |
# File 'lib/rigor/builtins/hkt_builtins.rb', line 64 def json_value_definition Rigor::Inference::HktRegistry.definition_with_body_tree( uri: :"json::value", params: [:K], body_tree: json_value_body_tree, source_path: __FILE__, source_line: __LINE__ - 5 ) end |
.json_value_registration ⇒ Object
55 56 57 58 59 60 61 62 |
# File 'lib/rigor/builtins/hkt_builtins.rb', line 55 def json_value_registration Rigor::Inference::HktRegistry::Registration.new( uri: :"json::value", arity: 1, variance: [:out], bound: Rigor::Type::Combinator.untyped ) end |
.method_return_override(class_name:, method_name:, kind:, arg_types: nil, hkt_registry: nil) ⇒ Rigor::Type?
Returns the reduced HKT type for
the given (class_name, method_name, kind) triple,
or nil when no built-in override is registered.
When arg_types is supplied AND the entry carries a
:discriminator symbol, the discriminator may swap
the spec's default args for an alternate (e.g.
JSON.parse(str, symbolize_names: true) discriminates
K = Symbol instead of the default K = String).
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 |
# File 'lib/rigor/builtins/hkt_builtins.rb', line 214 def method_return_override(class_name:, method_name:, kind:, arg_types: nil, hkt_registry: nil) spec = METHOD_RETURN_OVERRIDES[[class_name, method_name.to_sym, kind]] return nil unless spec args = discriminated_args(spec, arg_types) registration = hkt_registry&.registration(spec[:uri]) bound = registration&.bound || Rigor::Type::Combinator.untyped app = Rigor::Type::App.new(spec[:uri], args, bound: bound) reduced = if hkt_registry.nil? || !hkt_registry.defined?(spec[:uri]) app else hkt_registry.reduce(app) || app end apply_post_reduce(spec[:post_reduce], reduced, arg_types) end |
.permitted_class_nominals(value) ⇒ Object
Extract Singleton-class elements from a Tuple or
Array-shape carrier, mapping each to its Nominal
counterpart. Returns an empty array when no static
Singletons are reachable (e.g. value is Dynamic[T],
element types are non-Singleton, etc.).
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 |
# File 'lib/rigor/builtins/hkt_builtins.rb', line 318 def permitted_class_nominals(value) candidates = if value.is_a?(Rigor::Type::Tuple) value.elements elsif value.is_a?(Rigor::Type::Nominal) && value.class_name == "Array" && value.type_args.size == 1 element = value.type_args.first element.is_a?(Rigor::Type::Union) ? element.members : [element] else [] end candidates.filter_map do |c| c.is_a?(Rigor::Type::Singleton) ? Rigor::Type::Nominal.new(c.class_name) : nil end end |
.registry ⇒ Rigor::Inference::HktRegistry
Returns frozen registry
pre-seeded with all bundled HKT registrations +
bodies. Allocated fresh each call rather than
memoised — memoisation through a module-level
@registry ivar surfaces a Ractor::IsolationError
in pool workers (the ivar's contents include
HktBody::AppRef Symbol-keyed structures that the
current Ractor shareability audit hasn't yet been
walked through). The registry is small enough that
per-Environment construction is acceptable; an
eager-frozen constant is a future optimisation
once ADR-15 phase 4b.x covers the dependency graph.
105 106 107 108 109 110 |
# File 'lib/rigor/builtins/hkt_builtins.rb', line 105 def registry Rigor::Inference::HktRegistry.new( registrations: [json_value_registration, csv_parsed_registration], definitions: [json_value_definition, csv_parsed_definition] ) end |