codecs: Encode.auto and Decode.auto, derived from the type - #116
Merged
Conversation
Encode.auto : a -> string is a run-time helper reading a value's shape
from the emitted classes; Decode.auto : Decoder a is type-directed
lowering after inference: the checker records each site's Decoder a,
resolves it once inference is complete into a Codec (records, sum
types, tuples, List/Set/Map/Option/Result, newtypes read through,
recursion via a per-module table), and lowering emits a descriptor the
new _pf_dec_auto interpreter runs. A site whose type is still open is
an error naming the ways to pin it; its variable stays weak at let-
generalization so a later use pins it (the deferred-field discipline).
Wire shape: internally tagged objects ({"type": "Move", "fields":
[...]}, {"type": "Resign"}), Option as null or the value, tuples and
List/Set as arrays, a Map with string keys as an object and any other
as a list of pairs. The IR gains a Dict display and one-element tuples
now emit with their trailing comma. Plumbing: types::Codecs rides from
check_collecting / check_module_collecting into lower_collecting /
lower_in_project. Lesson 12 gets a derived-codecs section; DESIGN §6,
INTERNALS and ROADMAP item 19 updated.
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.
Closes #110.
Derived codecs
Encode.auto : a -> stringandDecode.auto : Decoder aare derived from the type, so a program that speaks to itself over a wire (or a save file) writes both ends in one line each, and the property the tests state once holds:Decode.decodeString Decode.auto (Encode.auto v) == Ok vfor records, sum types, tuples,List,Set,Map,Option,Result, newtypes (read through to the underlying value) and any recursion through them.Wire shape (decided in ROADMAP item 19): internally tagged objects, the serde / Pydantic / System.Text.Json convention. A record is an object keyed by its Pyfun field names; a case is
{"type": "Move", "fields": ["K11 a QUIZ"]}({"type": "Resign"}when nullary);Optionisnullor the value; tuples andList/Setare arrays; aMapwith string keys is an object and any other key type a list of[key, value]pairs.Two mechanisms
Encode.autois a run-time helper: the emitted classes already carry a record's field names and a case's class name, so_pf_enc_valuereads the value's shape viadataclasses.fields(a keyword-mangledclass_travels asclass) andjson.dumpsthe result.Decode.autois type-directed lowering after inference. The checker records everyDecode.autosite with its instantiatedDecoder a, resolvesaonce inference is complete, and derives aCodecfrom the declarations (record fields, constructor payloads at the instantiated type, newtypes read through). Lowering turns it into a descriptor the emitted_pf_dec_autointerprets; user-declared types live in a per-module_pf_codecstable under their displayed type, so a recursiveTreeis("ref", "Tree"):The derived decoder is a
Decoderlike any other (a callable that raises on a mismatch, strict like the primitives:unknown case Nope,expected an object, got list), so it composes withDecode.field/map2/andThenand runs underdecodeString'stry.Open sites. An
astill open at the site is an error naming the ways to pin it. Because the codec comes from the resolved type, a site's variable stays weak atlet-generalization (OCaml's'_a, the discipline a deferred field access already follows), solet back = Decode.decodeString Decode.auto wireat the top level is pinned by thematchthat follows. A function, an extern type, a lazySeqor anAsynchave no JSON form and say so. Cross-module types work when the declaring module is imported directly (the descriptor namesrules.View).Elsewhere
types::Codecsrides fromcheck_collecting/check_module_collectingintolower_collecting/lower_in_projectbeside the float-literal spans. The Python IR gains aDictdisplay, and one-element tuples now emit with their trailing comma (("str",); the old("str")was a parenthesised string).DESIGN.md§6 gets the "Derived codecs" paragraph, INTERNALS the plumbing note, lesson 12 a derived-codecs section (verified), ROADMAP item 19 is closed. Not done, and recorded: a composableEncode(Encode.object, aJsonvalue type) for shapes that are not a Pyfun type's own; a hand-written decoder remains the tool at a boundary you do not control.Tests
Typecheck: the weak-variable pinning (top-level
letthenmatch; inside a function), the open-site error and the function/extern-type errors,Encode.autopure and polymorphic. E2E: the rich round trip above with the descriptor table asserted and five error messages, a newtype plus keyword-named field, and a cross-module project round trip (tests/project.rs).cargo test,cargo clippy --all-targets,cargo fmt --checkanddocs/verify_lessons.pyare clean.