Row-to-row profunctors: Record/Variant merge directions, Shutter/Reel strengths, variant type sugar#53
Open
erykciepiela wants to merge 160 commits into
Open
Row-to-row profunctors: Record/Variant merge directions, Shutter/Reel strengths, variant type sugar#53erykciepiela wants to merge 160 commits into
erykciepiela wants to merge 160 commits into
Conversation
… page Make the V→V widgets (request/modal/notification) a request→response rename (input case `from` → output case `to`, same payload), so submit → thankYou and cancel → cancelled. The VariantToRecord stage then renders those into the result page, and checkout resolves to Record (email, cardNumber, savePayment) -> Record (thankYou :: Record …, cancelled :: Record …) — a checkout status / thank-you page carrying the placed (or cancelled) order. Still annotation-free (only @l VTAs). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ilure) `request` drops its second (`@to`) type parameter: its response variant is left deferred (forall w), so one request can resolve to several cases — here thankYou or failure — inferred from the page's handlers. The checkout sends submit through request alone (a deferred response can't share a V→V merge) and renders thankYou/ failure on the result page; a single signature pins the faked response payloads. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…le action set `request` becomes fully deferred on both sides (forall v w): the backend takes whatever action variant comes in and answers with whatever response variant, pinned to a concrete contract at the use site. The checkout adds a cancel button (submit/cancel buttons merge), sends the action set through request, and renders thankYou | failure | cancelled on the result page. The one use-site annotation on request is its backend contract — it also fixes the button merge and the payloads. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Pin statusBar/eventLog to a String-carrying case (Cons l String () r), so the result page is a record of String status messages. The request contract returns thankYou/failure/cancelled as Strings; checkout resolves to Record (email, cardNumber, savePayment) -> Record (thankYou::String, failure::String, cancelled::String). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`request` now processes only `submit` (→ thankYou | failure, deferred); `cancel` is routed to a local `modal` in the same VariantToVariant.do merge and turned straight into `cancelled` — it never reaches the backend. `modal`/`notification` become payload-changing case transforms (from::a → to::b) so cancel's form maps to a String message. Both V→V handlers are pinned to their contract (which also fixes the button merge); the page resolves to thankYou/failure/cancelled Strings. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add `actionButton` (Record () -> Variant): reads nothing, fires a case with an empty Record () payload. Use it for cancel, so cancel :: Record () instead of the whole form; modal maps cancel(Record ()) -> cancelled(String). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…type args) Drop the @from/@to Cons-label params from modal and notification; they're now forall v w (like request), so a use-site annotation pins them with no visible type arguments. checkout's modal loses its @"cancel" @"cancelled" VTAs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add Resolving/Retaining instances to the fake carrier and two signed checkout-domain scenarios — confirmPayment (Shutter, resolve, ×→+ loop step) and runningTotal (Reel, retain, +→× Mealy step) — making Done/Loop and output+state visible in the types. Document both in the showcase README. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ages Place the mixed strengths inside the direction-matching primitives: submit is now a Shutter (shutter, ×→+ with an editing loop case) and statusBar/ eventLog are Reels (reel, +→× retaining state). checkout uses them, so Shutter/Reel are incorporated without artificial stages. request/modal stay +→+ (neither). Drop the orphan confirmPayment/runningTotal bindings; update README to explain the direction constraint. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- bump purs to 0.15.16-variant.2 (the [ ... ] sugar → Prim.Variant.Variant) - point variant dep at local fork where Data.Variant.Variant = Prim.Variant.Variant - rewrite checkout's VariantToVariant annotations to [ ... ], dropping the Data.Variant import Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…o .5 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Use the variant compiler's `[ … ]` variant sugar and PureScript's `{ … }`
record sugar uniformly for all row types:
Record (a :: T) -> { a :: T } Variant (a :: T) -> [ a :: T ]
Record r -> { | r } Variant r -> [ | r ]
Record () -> {} Variant () -> []
Covers the four row-profunctor direction modules, Row.purs, Default.purs,
Example.purs, Commons.purs, Data.Default, Web.purs, and doc/row-profunctors.md
(prose type expressions and code fences). Drops now-unused `Variant` imports.
Class names, method names, and qualified `Record.*` refs are left untouched.
Also delete the stale showcase/README.md (referenced a deleted Logic.purs).
spago build succeeds with no warnings.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…gar in tests Add doc/variant-sugar.md describing the forked purs (0.15.16-variant.6) and its three variant sugar forms (type [ … ], constructor .label, pattern case _ of .label), including why purescript-variant is still required (the fork is purely syntactic) and where the value sugar applies (literal labels only; total case_ eliminators kept for exhaustiveness). Link it from README and add a syntax note to row-profunctors.md. Adopt constructor sugar in concrete test code: inj (Proxy @"lit") x -> .lit x in test/Main.purs and test/RestaurantReel.purs (dropping now-unused inj/Proxy imports). Total case_ # on eliminators left as-is. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… imports Replace every `Variant ( … )` type annotation in the test suite with the fork's [ … ] sugar (= Prim.Variant.Variant), eliminating the last project-wide references to the Data.Variant.Variant type name. test/Main.purs and test/BusinessOptics.purs no longer import Data.Variant at all — Eq/Show instances for assertEqual resolve transitively. RestaurantReel/EntityEventExample keep only (case_, on) for their total eliminators. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…n tests The earlier text claimed only the type sugar [ … ] was used and the constructor/pattern forms had no applicable site. That contradicts the later adoption of .label construction in test/Main.purs and test/RestaurantReel.purs and the project-wide elimination of the Variant type name. Replace with a per-form adoption table reflecting the actual state. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ithRecordOutputDefault Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… naming in demo Commons.purs sticks to the naming convention: case for variants (case_ / case_', since `case` is reserved), property for records. demo/1/Model.purs still referenced the long-gone `field` lens (unnoticed because demo/ is outside spago sources) — now `property`. Co-Authored-By: Claude Fable 5 (1M context) <noreply@anthropic.com>
… transformers, drop unlawful case_'
case_ and property now state p a a -> p [ | s ] [ | s ] / p { | s } { | s }
directly instead of the Prism/Lens synonyms. case_' removed: unused, and
its always-succeeding fallback broke the prism round-trip law.
Co-Authored-By: Claude Fable 5 (1M context) <noreply@anthropic.com>
…rection modules editProperty/editCase and Commons' property/case_ were the same functions under two names. Single canonical home now: property in RecordToRecord, case_ in VariantToVariant, both in explicit Strong/Choice transformer form. Commons keeps only its Maybe/Either helpers; input delegates to property. EntityEventExample qualifies Data.Variant.case_ to avoid the new clash. Co-Authored-By: Claude Fable 5 (1M context) <noreply@anthropic.com>
…erty, type-changing property/case_ propertyToCase (on resolve) and caseToProperty (on retain) are the mixed- direction analogues of property/case_: the wrapped p a b runs on the focused label l, the leftover row crosses the x/+ boundary wrapped under case/field w. property/case_ generalized to type-changing p a b -> p s t (prop was natively so; case_ rebuilt on prismE), subsuming lensProperty/prismCase — deleted. Co-Authored-By: Claude Fable 5 (1M context) <noreply@anthropic.com>
…ant/recordToCase/caseToRecord The whole-row-at-one-end family, named by what the wrapped profunctor consumes -> produces (record/variant = whole row, property/case = single label): introduceProperty -> recordToProperty (desugared to explicit transformer form), case_' -> caseToVariant (now takes p a [ | t ] — g dispatches into the output variant itself, dropping the @w parameter), plus new recordToCase (x->+, plain rmap (inj l) — the introduceCase the diagonals can't have) and caseToRecord (+->x, Mealy reducer on Retaining). eliminateCase removed: it is caseToVariant with a Void-pinned sink. introduceProperty/eliminateProperty/eliminateCase constraints minimized to Strong/Choice before the rename (bodies never used the row classes). Co-Authored-By: Claude Fable 5 (1M context) <noreply@anthropic.com>
…r the strengths StrongRecordToRecord/ChoiceVariantToVariant/ResolvingRecordToVariant/ RetainingVariantToRecord each had a single universal catch-all instance, which forecloses carrier overrides — a class with no instance diversity and no abstraction is a function in disguise. focusRecord/focusVariant are now plain functions over Strong/Choice; shutterWrap/reelWrap over Resolving/Retaining, gaining visible @w application (Proxy argument dropped — the instance-method limitation is gone with the class). Each direction module is now two layers: merge class + bare strength. Co-Authored-By: Claude Fable 5 (1M context) <noreply@anthropic.com>
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.
Summary
Builds a row-profunctor layer over
Record/Varianton top of the core UI profunctor, replacing the earlierChoiceLike/StrongLikeexperiments (both deleted).Row profunctors (
src/Data/Profunctor/Row/+Row.purs)Each of the four direction modules stacks three layers (merge → strength → single-field combinators):
RecordToRecord.purs(×→×) — mergerecordToRecord; row-typedStrong(focusRecordon a sub-Record carrying the complement);introduceProperty/eliminateProperty/editProperty; default liftswithRecordDefault/withRecordOutputDefaultVariantToVariant.purs(+→+) — mergevariantToVariant; row-typedChoice(focusVarianton a sub-Variant);eliminateCase/editCaseRecordToVariant.purs(×→+) —resolve, a loop/iteration step (Shutter:Either b c=Done/Loop);resolvePropertyVariantToRecord.purs(+→×) —retain, a Mealy/coroutine step (Reel:Tuple b c= output + next state);retainCaseRow.purs— umbrellaRowclass +Union-based widen/narrow reshapingsThe mixed strengths deliberately have no
(->)instance — a stateless function can't loop or retain state.Forked compiler with variant sugar
Switches the toolchain to
purescript-0.15.16-variant, adding[ case :: T, … ]variant type sugar and constructor sugar, adopted across library, tests, and docs. Seedoc/variant-sugar.md.Showcase & examples
showcase/— a realistic reactive checkout screen processed through all four merge directions: fake backend round-trip (request), cancel path, modal/notification, Shutter/Reel scenariossrc/Data/Profunctor/Row/Example.purs— widget vocabulary (textInput,checkbox,submit, …) at the row-profunctor levelBusinessOptics,EntityEventExample,HelloShutterReel,RestaurantReelDocs
doc/row-profunctors.md— design write-up for the whole layerdoc/variant-sugar.md,CLAUDE.mdNet: +2371 / −1379 across 30 files, 154 commits.
🤖 Generated with Claude Code