tricu

A lazy functional language based on Tree Calculus
Log | Files | Refs | README | LICENSE

catalogue.json (39532B)


      1 [
      2   {
      3     "id": "names-values",
      4     "title": "Names and values",
      5     "category": "Basic Examples",
      6     "hash": "4116a3702923898d5fafc8c855fbea38a7fd740bdb15667ef53fec7268b41381",
      7     "source": "!export *\n\n-- Welcome to tricu! Anything after -- on a line is a comment.\n-- tricu is a lazy functional language based on tree calculus.\n\n-- Click or tap underlined definition names to inspect their values.\n-- Function calls can have names too; we'll try that in \"Your first function\".\n-- Edits compile after a pause. IO programs offer \"Run IO\" inside their card.\n\n-- tricu offers string literals,\ngreeting = \"Hello, world!\"\n\n-- natural number literals (whole numbers starting at zero),\nanswer = 42\n\n-- list literals,\ngreetAndAnswer = [greeting answer]\n\n-- and more! Choose \"Doing arithmetic\" from the \"Program\" dropdown next.\n",
      8     "sourceSha256": "f91426877f7cbd74aa4de3f8241844e38bd724ed1c3766ee50e04f548a6a31fd",
      9     "export": "greeting",
     10     "arguments": [],
     11     "output": "string",
     12     "mode": "pure",
     13     "presets": {
     14       "greeting": {
     15         "arguments": [],
     16         "output": "string",
     17         "mode": "pure"
     18       },
     19       "answer": {
     20         "arguments": [],
     21         "output": "number",
     22         "mode": "pure"
     23       },
     24       "greetAndAnswer": {
     25         "arguments": [],
     26         "output": "values",
     27         "mode": "pure"
     28       }
     29     },
     30     "environment": "empty"
     31   },
     32   {
     33     "id": "arithmetic",
     34     "title": "Doing arithmetic",
     35     "category": "Basic Examples",
     36     "hash": "ac504497f656b84b7ae1e7f9a00086c76d05686df3eee2c5612a8c7f51fbf141",
     37     "source": "!import bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 *\n!export *\n\n-- Let's do some arithmetic! Our import supplies add and mul (multiply).\n-- Function calls use spaces between the function and its inputs.\ntotal = add 5 7\nmultiplied = mul 5 7\n\n-- Parentheses make one calculation the input to another.\ngrouped = mul (add 5 7) 2\n\n-- Try changing the numbers to calculate the cost of two things you'd buy.\n-- Next: Your first function.\n",
     38     "sourceSha256": "de1aea17cb2cab0a386f491f1e08fe00aec48c80967740ee55756987c69d328d",
     39     "export": "total",
     40     "arguments": [],
     41     "output": "number",
     42     "mode": "pure",
     43     "presets": {
     44       "total": {
     45         "arguments": [],
     46         "output": "number",
     47         "mode": "pure"
     48       },
     49       "multiplied": {
     50         "arguments": [],
     51         "output": "number",
     52         "mode": "pure"
     53       },
     54       "grouped": {
     55         "arguments": [],
     56         "output": "number",
     57         "mode": "pure"
     58       }
     59     },
     60     "environment": "empty"
     61   },
     62   {
     63     "id": "functions",
     64     "title": "Your first function",
     65     "category": "Basic Examples",
     66     "hash": "6c85d85a51c0c3ed8fb9adc1a5c81c18313174b3f72371933e432f3f895c1667",
     67     "source": "!import bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 *\n!export *\n\n-- Give a calculation a name so we can reuse it with different inputs.\ndouble number = mul number 2\n\n-- number is a parameter: each call supplies its value.\nsixDoubled = double 6\nnineDoubled = double 9\n\n-- A function can take more than one input.\nrectangle width height = mul width height\narea = rectangle 4 7\n\n-- Try writing triple and using it in place of double.\n-- Next: Working with lists.\n",
     68     "sourceSha256": "3e07457439457ad90533a9e1ecee410d3648d001b7308c6e8aaf41f03644e2f7",
     69     "export": "sixDoubled",
     70     "arguments": [],
     71     "output": "number",
     72     "mode": "pure",
     73     "presets": {
     74       "sixDoubled": {
     75         "arguments": [],
     76         "output": "number",
     77         "mode": "pure"
     78       },
     79       "nineDoubled": {
     80         "arguments": [],
     81         "output": "number",
     82         "mode": "pure"
     83       },
     84       "double": {
     85         "arguments": [
     86           {
     87             "type": "number",
     88             "value": "6"
     89           }
     90         ],
     91         "output": "number",
     92         "mode": "pure"
     93       },
     94       "rectangle": {
     95         "arguments": [
     96           {
     97             "type": "number",
     98             "value": "4"
     99           },
    100           {
    101             "type": "number",
    102             "value": "7"
    103           }
    104         ],
    105         "output": "number",
    106         "mode": "pure"
    107       },
    108       "area": {
    109         "arguments": [],
    110         "output": "number",
    111         "mode": "pure"
    112       }
    113     },
    114     "environment": "empty"
    115   },
    116   {
    117     "id": "lists",
    118     "title": "Working with lists",
    119     "category": "Basic Examples",
    120     "hash": "8eb72ff6766fb762614c6dca9026ebda78d06f5e8f78e48cc29b195ba2154b21",
    121     "source": "!import bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 *\n!export *\n\n-- Collect values in a list. Spaces separate items; [] is the empty list.\nnumbers = [2 4 6]\nitemCount = length numbers\ntotal = sum numbers\n\n-- Try adding another item to numbers, then try numbers = [].\n-- Next: Transforming a list.\n",
    122     "sourceSha256": "ba33f8097d479eeb3adcbcc92c7b33931970df2b7ea4d523535673a095e5a1b2",
    123     "export": "itemCount",
    124     "arguments": [],
    125     "output": "number",
    126     "mode": "pure",
    127     "presets": {
    128       "itemCount": {
    129         "arguments": [],
    130         "output": "number",
    131         "mode": "pure"
    132       },
    133       "total": {
    134         "arguments": [],
    135         "output": "number",
    136         "mode": "pure"
    137       },
    138       "numbers": {
    139         "arguments": [],
    140         "output": "values",
    141         "mode": "pure"
    142       }
    143     },
    144     "environment": "empty"
    145   },
    146   {
    147     "id": "transform-list",
    148     "title": "Transforming a list",
    149     "category": "Basic Examples",
    150     "hash": "639c8d3959320ef99d9416257905f189519a90ea4b315cdbb0485a386e46191c",
    151     "source": "!import bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 *\n!export *\n\n-- Functions are values too. We can pass one to another function.\ndouble number = mul number 2\nnumbers = [2 4 6]\n\n-- map takes a function, then a list, and applies it to every item.\ndoubled = map double numbers\n-- map builds a new list; the original numbers are unchanged.\ntotal = sum doubled\n\n-- Try multiplying by 3 instead.\n-- Next: Making a choice.\n",
    152     "sourceSha256": "e752330fcedfc61065c695ebbafa1e6bc65745d890ee98c7682d4e5fe11bc45f",
    153     "export": "total",
    154     "arguments": [],
    155     "output": "number",
    156     "mode": "pure",
    157     "presets": {
    158       "total": {
    159         "arguments": [],
    160         "output": "number",
    161         "mode": "pure"
    162       },
    163       "double": {
    164         "arguments": [
    165           {
    166             "type": "number",
    167             "value": "5"
    168           }
    169         ],
    170         "output": "number",
    171         "mode": "pure"
    172       },
    173       "numbers": {
    174         "arguments": [],
    175         "output": "values",
    176         "mode": "pure"
    177       },
    178       "doubled": {
    179         "arguments": [],
    180         "output": "values",
    181         "mode": "pure"
    182       }
    183     },
    184     "environment": "empty"
    185   },
    186   {
    187     "id": "choices",
    188     "title": "Making a choice",
    189     "category": "Basic Examples",
    190     "hash": "dd0931401769303bf2e1ce08c51e7778779ccc9643ca8315c256c0542eedff7a",
    191     "source": "!export *\n\n-- Choose a message by matching an input.\nmessage 0 = \"Your basket is empty.\"\nmessage 1 = \"You have one item.\"\nmessage _ = \"You have several items.\"\n\n-- These equations are tried from top to bottom.\n-- _ matches any remaining input: this is the fallback.\nemptyBasket = message 0\noneItem = message 1\nfullBasket = message 4\n\n-- Try adding a message for 2 items before the fallback equation.\n-- Keep the equations together so they define one function.\n-- Next: a Practical Example, or Language reference.\n",
    192     "sourceSha256": "b3e3cbfdf958715f14dca4bfd0b69e58a6e1b250ee080cbddfa29b0711e0961d",
    193     "export": "emptyBasket",
    194     "arguments": [],
    195     "output": "string",
    196     "mode": "pure",
    197     "presets": {
    198       "emptyBasket": {
    199         "arguments": [],
    200         "output": "string",
    201         "mode": "pure"
    202       },
    203       "oneItem": {
    204         "arguments": [],
    205         "output": "string",
    206         "mode": "pure"
    207       },
    208       "fullBasket": {
    209         "arguments": [],
    210         "output": "string",
    211         "mode": "pure"
    212       },
    213       "message": {
    214         "arguments": [
    215           {
    216             "type": "number",
    217             "value": "0"
    218           }
    219         ],
    220         "output": "string",
    221         "mode": "pure"
    222       }
    223     },
    224     "environment": "empty"
    225   },
    226   {
    227     "id": "language-reference",
    228     "title": "Language reference",
    229     "category": "Language & Runtime Explorations",
    230     "hash": "86717d7f9d69fe730c6c6172c42c49895623aa070bf675e1b5f751adb30616c1",
    231     "source": "!import bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 *\n!import 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db as Math\n!export *\n\n-- The open base import supplies contract helpers and unqualified names.\n-- Math is a qualified import: its names start with Math.\n-- A syntax sampler: independent examples, rather than one application.\n-- Start with answer: its value is 42.\n-- Full contracts: follow For AI assistants in the footer to Language features.\n-- Comments give sample calls and interpretations; name a call to try it.\n\n-- Names, lambdas, named parameters, application and grouping.\nidentity = (x : x)\nfirst a b = a\nanswer = identity (first 42 7)\n-- identity \"hello\" / Text -> hello; first 7 9 / Natural number -> 7.\n\n-- The kernel constructors: Leaf, Stem and Fork. Use tricu for arbitrary trees.\nleaf = t\nshapes = [t (t t) (t t t)]\n-- leaf / tricu -> t; shapes / tricu shows a list containing all three shapes.\nliterals = [\"hello\" 42 [1 2 3]]\n-- literals / tricu shows mixed values. Strings support Unicode; names are ASCII.\n-- Lists use spaces. Group application items: [(identity 1) (identity 2)].\n\n-- Partial application: provide one of a function's arguments now.\naddTwo = Math.add 2\n-- addTwo 5 / Natural number -> 7.\n\n-- Ordered equations: literals, a pin, exact lists, cons and wildcard.\n-- A pin (^true) compares against an existing value.\ndescribe 7 = \"seven\"\ndescribe \"hi\" = \"greeting\"\ndescribe ^true = \"true\"\ndescribe [a b] = \"two items\"\ndescribe [head | tail] = \"nonempty list\"\ndescribe [] = \"empty list\"\ndescribe _ = \"other\"\n-- Try each with Text: describe 7, describe \"hi\", describe (t t),\n-- describe [4 5], describe [1 2 3], describe [], describe (t (t t)).\n\n-- Pair patterns and whole-value capture. Construct a pair with t left right.\nkeep whole{left, right} = whole\nkeep _ = t\n-- keep (t t t) / tricu -> (t t t); keep (t t) / tricu -> t.\n\n-- Plain local bindings are sequential and nonrecursive.\nlocal x = let\n  double n = Math.multiply n 2\n  in double x\nviaWhere x = step x where step y = Math.add y 1\n-- local 6 -> 12; viaWhere 6 -> 7, both Natural number.\n\n-- Direct self-recursion, with a final fallback equation.\ncopy [] = []\ncopy [head | tail] = t head <| copy tail\ncopy _ = []\n-- copy [1 2 3] / Bytes -> [1, 2, 3]. Bytes displays naturals from 0 through 255.\n-- Use tricu for a general list; copy [] and copy (t t) both return [].\n\n-- case examines an intermediate value; arms align and finish with a fallback.\nfirstOr fallback xs = case xs of\n  [head | _] = head\n  _ = fallback\n-- firstOr 99 [5] -> 5; firstOr 99 [] -> 99, both Natural number.\n\n-- Local self-recursion is explicit: let rec (also available with where rec).\nlocalCopy xs = let rec\n  walk [] = []\n  walk [head | tail] = t head <| walk tail\n  walk _ = []\n  in walk xs\n-- localCopy [4 5] / Bytes -> [4, 5]; localCopy [] -> [].\n\n-- Parameter and result contracts check values at runtime.\nchecked x@nat? =@nat? x\n-- checked 42 / Natural number -> 42. A failed contract produces a Result.\n-- Keep annotations on plain function heads; see Contracts for Result handlers.\n\n-- Pipes and do use an explicit sequencing operator. Here a lambda passes a value\n-- to the next continuation. It is ordinary pure code; IO uses IO.bind instead.\nsequenced input = do (value next : next value)\n  value <- input |> identity\n  identity value\n  identity <| value\n-- sequenced 9 / Natural number -> 9.\n-- Both pipe chains associate left-to-right; group nested applications explicitly.\n\n|- Block comments are another way to annotate source. They do not nest. -|\n-- !export * selects local definitions. Use !export name for a smaller interface.\n-- An open import uses !import MANIFEST *; a reexport uses !export * from MANIFEST.\n-- Replace MANIFEST with the full identity from Explore libraries or the library guide.\n-- Reexports select the public interface; imports introduce names into scope.\n-- See Conversation for IO, and the docs for Maybe/Result/parser protocols.\n",
    232     "sourceSha256": "d464c7964012c904cc322e957990be26c4172e11eea745ef5f75d4cd03a6635f",
    233     "export": "answer",
    234     "arguments": [],
    235     "output": "number",
    236     "mode": "pure",
    237     "presets": {
    238       "answer": {
    239         "arguments": [],
    240         "output": "number",
    241         "mode": "pure"
    242       },
    243       "identity": {
    244         "arguments": [
    245           {
    246             "type": "number",
    247             "value": "7"
    248           }
    249         ],
    250         "output": "number",
    251         "mode": "pure"
    252       },
    253       "first": {
    254         "arguments": [
    255           {
    256             "type": "number",
    257             "value": "7"
    258           },
    259           {
    260             "type": "number",
    261             "value": "9"
    262           }
    263         ],
    264         "output": "number",
    265         "mode": "pure"
    266       },
    267       "leaf": {
    268         "arguments": [],
    269         "output": "tricu",
    270         "mode": "pure"
    271       },
    272       "shapes": {
    273         "arguments": [],
    274         "output": "tricu",
    275         "mode": "pure"
    276       },
    277       "literals": {
    278         "arguments": [],
    279         "output": "tricu",
    280         "mode": "pure"
    281       },
    282       "addTwo": {
    283         "arguments": [
    284           {
    285             "type": "number",
    286             "value": "5"
    287           }
    288         ],
    289         "output": "number",
    290         "mode": "pure"
    291       },
    292       "describe": {
    293         "arguments": [
    294           {
    295             "type": "number",
    296             "value": "7"
    297           }
    298         ],
    299         "output": "string",
    300         "mode": "pure"
    301       },
    302       "keep": {
    303         "arguments": [
    304           {
    305             "type": "tree",
    306             "value": "200"
    307           }
    308         ],
    309         "output": "tricu",
    310         "mode": "pure"
    311       },
    312       "local": {
    313         "arguments": [
    314           {
    315             "type": "number",
    316             "value": "6"
    317           }
    318         ],
    319         "output": "number",
    320         "mode": "pure"
    321       },
    322       "viaWhere": {
    323         "arguments": [
    324           {
    325             "type": "number",
    326             "value": "6"
    327           }
    328         ],
    329         "output": "number",
    330         "mode": "pure"
    331       },
    332       "copy": {
    333         "arguments": [
    334           {
    335             "type": "list",
    336             "value": [
    337               {
    338                 "type": "number",
    339                 "value": "1"
    340               },
    341               {
    342                 "type": "number",
    343                 "value": "2"
    344               },
    345               {
    346                 "type": "number",
    347                 "value": "3"
    348               }
    349             ]
    350           }
    351         ],
    352         "output": "bytes",
    353         "mode": "pure"
    354       },
    355       "firstOr": {
    356         "arguments": [
    357           {
    358             "type": "number",
    359             "value": "99"
    360           },
    361           {
    362             "type": "list",
    363             "value": [
    364               {
    365                 "type": "number",
    366                 "value": "5"
    367               }
    368             ]
    369           }
    370         ],
    371         "output": "number",
    372         "mode": "pure"
    373       },
    374       "localCopy": {
    375         "arguments": [
    376           {
    377             "type": "list",
    378             "value": [
    379               {
    380                 "type": "number",
    381                 "value": "4"
    382               },
    383               {
    384                 "type": "number",
    385                 "value": "5"
    386               }
    387             ]
    388           }
    389         ],
    390         "output": "bytes",
    391         "mode": "pure"
    392       },
    393       "checked": {
    394         "arguments": [
    395           {
    396             "type": "number",
    397             "value": "42"
    398           }
    399         ],
    400         "output": "number",
    401         "mode": "pure"
    402       },
    403       "sequenced": {
    404         "arguments": [
    405           {
    406             "type": "number",
    407             "value": "9"
    408           }
    409         ],
    410         "output": "number",
    411         "mode": "pure"
    412       }
    413     },
    414     "environment": "empty"
    415   },
    416   {
    417     "id": "greet",
    418     "title": "A conversation",
    419     "category": "Practical Examples",
    420     "hash": "b6f32449d85257fbd0a589d29ef05ea4a503c4a7e6105926d408b0551a34fd77",
    421     "source": "!import 3dbb76ee4b350c161ed4f9c9f600034195b2fcf99202d46b8adcd25715a56f6b as IO\n!import 11791107bece9dac78ff92dcec75d11830921e359976f561a934b36f9e8910df as Text\n!export main\n\n-- A conversation: ask for a name, then greet the person who replies.\n-- Send a line or choose \"End input\" in the card's terminal.\n-- Read and write Results are checked before continuing.\n-- Return the name when IO finishes. Try changing the greeting.\ngreet writer [[\"line\" name _] _] = do IO.require\n  Text.writeText writer \"Hello, \"\n  Text.writeText writer name\n  Text.writeText writer \"!\\n\"\n  IO.pure name\ngreet writer [[\"eof\"] _] = IO.require\n  (Text.writeText writer \"End of input.\\n\") (_ : IO.pure \"\")\ngreet _ _ = IO.fail \"Invalid line reply\"\n\nmain = IO.io\n  do IO.require\n    reader <- IO.stdin\n    writer <- IO.stdout\n    Text.writeText writer \"What is your name?\\n\"\n    line <- Text.readLine (Text.lineCursor reader) 4096\n    greet writer line\n",
    422     "sourceSha256": "269f5ee9dcfa6cada4d18597fb35b2a1354535f204d38e3aab7d03621ad7adad",
    423     "export": "main",
    424     "arguments": [],
    425     "output": "string",
    426     "mode": "io",
    427     "presets": {},
    428     "environment": "empty"
    429   },
    430   {
    431     "id": "microscope",
    432     "title": "Tree microscope",
    433     "category": "Language & Runtime Explorations",
    434     "hash": "493a125b649639c0df8348b4aeb3a455b7139cae0634ba114866c48813500bc5",
    435     "source": "!import bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 *\n!export shape\n!export size\n!export same\n\n-- Inspect data OR compiled program trees, not source text.\n-- Try shape (t t t), size (t t t), or same (t t) (t t).\n-- Also try shape (x : x): functions are inspectable trees too.\n-- triage dispatches directly on Leaf, Stem and Fork without visiting children.\n-- Arithmetic and Booleans come from base; traversal is the subject of this demo.\nshape tree = triage \"Leaf\" (child : \"Stem\") (left right : \"Fork\") tree\n\n-- Count every constructor occurrence, including repeated shared branches.\n-- This is expanded tree size, not allocated nodes or source-code length.\ncountNodes tree acc = triage\n  (succ acc)\n  (child : countNodes child <| succ acc)\n  (left right : succ acc |> countNodes left |> countNodes right)\n  tree\n\nsize tree = countNodes tree 0\n\nsame a b = triage\n  (triage true (ignored : false) (ignored ignored2 : false) b)\n  (x : triage false (y : same x y) (ignored ignored2 : false) b)\n  (x y : triage false (ignored : false) (u v : and? (same x u) (same y v)) b)\n  a\n",
    436     "sourceSha256": "ac4bd61b5953c523e29a42387acfc6305075066edf6c709283b02de19c51025c",
    437     "export": "size",
    438     "arguments": [
    439       {
    440         "type": "tree",
    441         "value": "21100"
    442       }
    443     ],
    444     "output": "number",
    445     "mode": "pure",
    446     "presets": {
    447       "shape": {
    448         "arguments": [
    449           {
    450             "type": "tree",
    451             "value": "21100"
    452           }
    453         ],
    454         "output": "string",
    455         "mode": "pure"
    456       },
    457       "size": {
    458         "arguments": [
    459           {
    460             "type": "tree",
    461             "value": "21100"
    462           }
    463         ],
    464         "output": "number",
    465         "mode": "pure"
    466       },
    467       "same": {
    468         "arguments": [
    469           {
    470             "type": "tree",
    471             "value": "21100"
    472           },
    473           {
    474             "type": "tree",
    475             "value": "21100"
    476           }
    477         ],
    478         "output": "bool",
    479         "mode": "pure"
    480       }
    481     },
    482     "environment": "empty"
    483   },
    484   {
    485     "id": "rgb",
    486     "title": "RGB validator",
    487     "category": "Practical Examples",
    488     "hash": "b8c0e21c09f63511794cc62827a6b231e62a55f7e779022a1b740aaba29b0368",
    489     "source": "!import bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 *\n!import 7a24af56a9ce9b947cadcafd3a9da127b1507275bfac62b9fa55e27490b176dc *\n!export main\n\n-- Validate an RGB list using the library's canonical-byte predicate.\n-- Try result = main [64 128 255].\n-- Try changing its input to [0 256 255], then [0 1].\n-- Canonical encoding matters: redundant high zero bits are rejected too.\n-- List-shape errors are separate from invalid channels; checks short-circuit.\nmain (t _) = \"Expected a list.\"\nmain [_ | (t _)] = \"Expected a list.\"\nmain [_ | [_ | (t _)]] = \"Expected a list.\"\nmain [r g b] = case and? (binaryByte? r) (and? (binaryByte? g) (binaryByte? b)) of\n  ^true = \"RGB triple accepted.\"\n  _ = \"A channel is not a canonical byte (0-255).\"\nmain _ = \"Expected exactly three channels.\"\n",
    490     "sourceSha256": "721a08c37746e7b982994a7981b8fcff7312d889dac3276e7e72ca8cc06ed87a",
    491     "export": "main",
    492     "arguments": [
    493       {
    494         "type": "bytes",
    495         "value": [
    496           64,
    497           128,
    498           255
    499         ]
    500       }
    501     ],
    502     "output": "string",
    503     "mode": "pure",
    504     "presets": {},
    505     "environment": "empty"
    506   },
    507   {
    508     "id": "shipping",
    509     "title": "Shipping policies",
    510     "category": "Practical Examples",
    511     "hash": "4aa9ce9f19dfce87ecb3f6bd0a2e881dafcbcd3d2e4b4075befbb378d80833ea",
    512     "source": "!import bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 *\n!export v1\n!export v2\n\n-- Shipping policy: arguments are destination, cart subtotal in cents, grams.\n-- Destinations: domestic, regional, international. Grams: 1..5000.\n-- Try result = v1 \"domestic\" 8000 1500.\n-- Change v1 to v2: domestic free shipping changes from $75 to $100.\n-- Otherwise domestic costs $4.99/$5.99, regional $9.99, international $19.99.\n-- Packages above 1kg add $2.50. Subtotals are bounded to $10,000.\n-- The bundle hash + export pins the policy used to reproduce a quote later.\n-- Records use [fieldName tag value]: 0 = text, 1 = natural, 2 = Boolean.\n-- Base supplies canonical-natural checks, arithmetic and Booleans.\n\nfailure message = [[\"status\" 0 \"error\"] [\"message\" 0 message]]\n\nbaseRate 1 \"domestic\" = 499\nbaseRate _ \"domestic\" = 599\nbaseRate _ \"regional\" = 999\nbaseRate _ _ = 1999\n\npaidRate version zone grams = case lte_? grams 1000 of\n  ^true = baseRate version zone\n  _ = add 250 <| baseRate version zone\n\nfreeThreshold 1 = 7500\nfreeThreshold _ = 10000\n\namount version zone subtotal grams = case and? (equal? zone \"domestic\") (lte_? (freeThreshold version) subtotal) of\n  ^true = 0\n  _ = paidRate version zone grams\n\nquote version zone subtotal grams = case and? (isNat? subtotal) (isNat? grams) of\n  ^true = case or? (equal? zone \"domestic\") (or? (equal? zone \"regional\") (equal? zone \"international\")) of\n    ^true = case and? (lte_? subtotal 1000000) (and? (lte_? 1 grams) (lte_? grams 5000)) of\n      ^true = [[\"status\" 0 \"ok\"] [\"policyVersion\" 1 version]\n        [\"destination\" 0 zone] [\"shippingCents\" 1 (amount version zone subtotal grams)]]\n      _ = failure \"Subtotal must be <=1000000 cents; grams must be 1..5000.\"\n    _ = failure \"Unknown destination.\"\n  _ = failure \"Subtotal and grams must be canonical natural numbers.\"\n\nv1 zone subtotal grams = quote 1 zone subtotal grams\nv2 zone subtotal grams = quote 2 zone subtotal grams\n",
    513     "sourceSha256": "790497b064d8093290af112d8c1cc072c93da25aea4f288e87d7abefa9750e7f",
    514     "export": "v1",
    515     "arguments": [
    516       {
    517         "type": "string",
    518         "value": "domestic"
    519       },
    520       {
    521         "type": "number",
    522         "value": "8000"
    523       },
    524       {
    525         "type": "number",
    526         "value": "1500"
    527       }
    528     ],
    529     "output": "tricu",
    530     "mode": "pure",
    531     "presets": {
    532       "v1": {
    533         "arguments": [
    534           {
    535             "type": "string",
    536             "value": "domestic"
    537           },
    538           {
    539             "type": "number",
    540             "value": "8000"
    541           },
    542           {
    543             "type": "number",
    544             "value": "1500"
    545           }
    546         ],
    547         "output": "tricu",
    548         "mode": "pure"
    549       },
    550       "v2": {
    551         "arguments": [
    552           {
    553             "type": "string",
    554             "value": "domestic"
    555           },
    556           {
    557             "type": "number",
    558             "value": "8000"
    559           },
    560           {
    561             "type": "number",
    562             "value": "1500"
    563           }
    564         ],
    565         "output": "tricu",
    566         "mode": "pure"
    567       }
    568     },
    569     "environment": "empty"
    570   },
    571   {
    572     "id": "events",
    573     "title": "Event adapters",
    574     "category": "Practical Examples",
    575     "hash": "8b51693c63658d59c8083ec1c95cf1f6565ce5799791f540e92caf508613c9fb",
    576     "source": "!import bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 *\n!import 3f70d95cf1bae0928ee89acd2a9e0cdd8287796efae32a9f08d33e3dd0149c55 *\n!export vendorA\n!export vendorB\n\n-- Event adapter: two producers, one consumer-facing record schema.\n-- vendorA accepts [eventName customer totalCents]. eventName is purchase/refund.\n-- vendorB accepts [customer refunded totalCents]. refunded is a Boolean.\n-- Both return schemaVersion, eventType, customer and amountCents.\n-- Try result = vendorA [\"purchase\" \"James\" 2499].\n-- Try result = vendorB [\"James\" false 2499] to describe the same event.\n-- A producer can publish the adapter hash alongside its event schema.\n-- Records use [fieldName tag value]: 0 = text, 1 = natural, 2 = Boolean.\n-- Base supplies arithmetic/Booleans; hostString? checks Unicode scalar text.\n\nfailure message = [[\"status\" 0 \"error\"] [\"message\" 0 message]]\n\nnormalized event customer cents = case hostString? customer of\n  ^true = case and? (isNat? cents) (lte_? cents 1000000) of\n    ^true = [[\"schemaVersion\" 1 1] [\"eventType\" 0 event]\n      [\"customer\" 0 customer] [\"amountCents\" 1 cents]]\n    _ = failure \"amountCents must be a canonical natural <=1000000.\"\n  _ = failure \"customer must be scalar text.\"\n\nfromA \"purchase\" customer cents = normalized \"order.created\" customer cents\nfromA \"refund\" customer cents = normalized \"order.refunded\" customer cents\nfromA _ _ _ = failure \"Unknown vendor A event.\"\n\nfromB customer ^true cents = normalized \"order.refunded\" customer cents\nfromB customer ^false cents = normalized \"order.created\" customer cents\nfromB _ _ _ = failure \"refunded must be a canonical Boolean.\"\n\n-- Preserve list-shape diagnostics without inspecting any field before dispatch.\nunpack handler (t _) = failure \"Expected a list.\"\nunpack handler [_ | (t _)] = failure \"Expected a list.\"\nunpack handler [_ | [_ | (t _)]] = failure \"Expected a list.\"\nunpack handler [a b c] = handler a b c\nunpack _ _ = failure \"Expected exactly three fields.\"\n\nvendorA values = unpack fromA values\nvendorB values = unpack fromB values\n",
    577     "sourceSha256": "3d086d7b0dba582e95e28515747c6d80cd4ab0af3c825af30fd759b1788527e3",
    578     "export": "vendorA",
    579     "arguments": [
    580       {
    581         "type": "list",
    582         "value": [
    583           {
    584             "type": "string",
    585             "value": "purchase"
    586           },
    587           {
    588             "type": "string",
    589             "value": "James"
    590           },
    591           {
    592             "type": "number",
    593             "value": "2499"
    594           }
    595         ]
    596       }
    597     ],
    598     "output": "tricu",
    599     "mode": "pure",
    600     "presets": {
    601       "vendorA": {
    602         "arguments": [
    603           {
    604             "type": "list",
    605             "value": [
    606               {
    607                 "type": "string",
    608                 "value": "purchase"
    609               },
    610               {
    611                 "type": "string",
    612                 "value": "James"
    613               },
    614               {
    615                 "type": "number",
    616                 "value": "2499"
    617               }
    618             ]
    619           }
    620         ],
    621         "output": "tricu",
    622         "mode": "pure"
    623       },
    624       "vendorB": {
    625         "arguments": [
    626           {
    627             "type": "list",
    628             "value": [
    629               {
    630                 "type": "string",
    631                 "value": "James"
    632               },
    633               {
    634                 "type": "bool",
    635                 "value": false
    636               },
    637               {
    638                 "type": "number",
    639                 "value": "2499"
    640               }
    641             ]
    642           }
    643         ],
    644         "output": "tricu",
    645         "mode": "pure"
    646       }
    647     },
    648     "environment": "empty"
    649   },
    650   {
    651     "id": "registration",
    652     "title": "Registration validator",
    653     "category": "Practical Examples",
    654     "hash": "679733e17a86cc5e3f5282ca824cad4c9c0febd260e51f6a87e16dd14c02c45a",
    655     "source": "!import bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 *\n!import 3f70d95cf1bae0928ee89acd2a9e0cdd8287796efae32a9f08d33e3dd0149c55 *\n!export main\n\n-- Registration validator: name, email, age, termsAccepted are separate arguments.\n-- Returns one field-specific message per input, plus an accepted Boolean.\n-- Try result = main \"James\" \"james@example.org\" 21 true.\n-- Try changing the age to 17 or the email to \"bad\" to see field messages.\n-- Name must be nonempty scalar text. Age must be a natural from 18 to 120.\n-- Email uses a deliberately small rule: nonempty text containing @ and a dot.\n-- Terms must be the canonical true Boolean. This is a teaching policy, not SMTP validation.\n-- Browser and server can pin this same executable instead of duplicating rules.\n-- Records use [fieldName tag value]: 0 = text, 1 = natural, 2 = Boolean.\n-- Base supplies arithmetic/Booleans; hostString? checks Unicode scalar text.\n\nnameMessage name = case hostString? name of\n  ^true = case name of\n    [] = \"Name is required.\"\n    _ = \"\"\n  _ = \"Name must be scalar text.\"\n\nemailMessage email = case hostString? email of\n  ^true = case and? (contains? \"@\" email) (contains? \".\" email) of\n    ^true = \"\"\n    _ = \"Email must contain @ and a dot.\"\n  _ = \"Email must be scalar text.\"\n\nageMessage age = case isNat? age of\n  ^true = case and? (lte_? 18 age) (lte_? age 120) of\n    ^true = \"\"\n    _ = \"Age must be from 18 to 120.\"\n  _ = \"Age must be a canonical natural.\"\n\ntermsMessage ^true = \"\"\ntermsMessage _ = \"Accept the terms.\"\n\nreport name email age terms = [\n  [\"accepted\" 2 (and? (equal? name \"\") (and? (equal? email \"\") (and? (equal? age \"\") (equal? terms \"\"))))]\n  [\"nameError\" 0 name] [\"emailError\" 0 email]\n  [\"ageError\" 0 age] [\"termsError\" 0 terms]]\n\nmain name email age terms = report (nameMessage name) (emailMessage email) (ageMessage age) (termsMessage terms)\n",
    656     "sourceSha256": "c91388231dfc9a04da02e750cfcbd873947f6cf761a59352446000421c7806dd",
    657     "export": "main",
    658     "arguments": [
    659       {
    660         "type": "string",
    661         "value": "James"
    662       },
    663       {
    664         "type": "string",
    665         "value": "james@example.org"
    666       },
    667       {
    668         "type": "number",
    669         "value": "21"
    670       },
    671       {
    672         "type": "bool",
    673         "value": true
    674       }
    675     ],
    676     "output": "tricu",
    677     "mode": "pure",
    678     "presets": {
    679       "main": {
    680         "arguments": [
    681           {
    682             "type": "string",
    683             "value": "James"
    684           },
    685           {
    686             "type": "string",
    687             "value": "james@example.org"
    688           },
    689           {
    690             "type": "number",
    691             "value": "21"
    692           },
    693           {
    694             "type": "bool",
    695             "value": true
    696           }
    697         ],
    698         "output": "tricu",
    699         "mode": "pure"
    700       }
    701     },
    702     "environment": "empty"
    703   },
    704   {
    705     "id": "migration",
    706     "title": "Profile migrations",
    707     "category": "Practical Examples",
    708     "hash": "730aeb8203ff504b7483999a6477a292b10871f82e94a77d3d5f7b49ecc3b6b3",
    709     "source": "!import bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 *\n!import 3f70d95cf1bae0928ee89acd2a9e0cdd8287796efae32a9f08d33e3dd0149c55 *\n!export v1\n!export v2\n\n-- Archived profile v0: [name email]. v1: adds schemaVersion and displayName.\n-- v2: also supplies marketingOptIn=false; existing data is preserved.\n-- Try result = v1 [\"James\" \"james@example.org\"].\n-- Change v1 to v2 to compare the two target schemas.\n-- A migration receipt can record the input identity, executable hash/export,\n-- output identity and wire schema. Running this demo stays entirely local.\n-- Records use [fieldName tag value]: 0 = text, 1 = natural, 2 = Boolean.\n-- Base supplies arithmetic/Booleans; hostString? checks Unicode scalar text.\n\nfailure message = [[\"status\" 0 \"error\"] [\"message\" 0 message]]\n\nmigrate version name email = case and? (hostString? name) (hostString? email) of\n  ^true = case version of\n    1 = [[\"schemaVersion\" 1 1] [\"displayName\" 0 name] [\"email\" 0 email]]\n    _ = [[\"schemaVersion\" 1 2] [\"displayName\" 0 name] [\"email\" 0 email]\n      [\"marketingOptIn\" 2 false]]\n  _ = failure \"Name and email must be scalar text.\"\n\nprofile _ (t _) = failure \"Expected a list.\"\nprofile _ [_ | (t _)] = failure \"Expected a list.\"\nprofile version [name email] = migrate version name email\nprofile _ _ = failure \"Expected [name email].\"\n\nv1 values = profile 1 values\nv2 values = profile 2 values\n",
    710     "sourceSha256": "239e45bde8f461360a016bfbcca3111dc72da3b7dc237736d05b12dc6018e9f8",
    711     "export": "v1",
    712     "arguments": [
    713       {
    714         "type": "list",
    715         "value": [
    716           {
    717             "type": "string",
    718             "value": "James"
    719           },
    720           {
    721             "type": "string",
    722             "value": "james@example.org"
    723           }
    724         ]
    725       }
    726     ],
    727     "output": "tricu",
    728     "mode": "pure",
    729     "presets": {
    730       "v1": {
    731         "arguments": [
    732           {
    733             "type": "list",
    734             "value": [
    735               {
    736                 "type": "string",
    737                 "value": "James"
    738               },
    739               {
    740                 "type": "string",
    741                 "value": "james@example.org"
    742               }
    743             ]
    744           }
    745         ],
    746         "output": "tricu",
    747         "mode": "pure"
    748       },
    749       "v2": {
    750         "arguments": [
    751           {
    752             "type": "list",
    753             "value": [
    754               {
    755                 "type": "string",
    756                 "value": "James"
    757               },
    758               {
    759                 "type": "string",
    760                 "value": "james@example.org"
    761               }
    762             ]
    763           }
    764         ],
    765         "output": "tricu",
    766         "mode": "pure"
    767       }
    768     },
    769     "environment": "empty"
    770   },
    771   {
    772     "id": "parser",
    773     "preparation": "native",
    774     "environment": "empty",
    775     "category": "Language & Runtime Explorations",
    776     "title": "Parser combinators",
    777     "hash": "355e41506f39e05da8c8a72a68aa810a486e062480322bac11af67e86980520e",
    778     "source": "!import 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 *\n!import 14282088469cc420dd014700da06998f6bb8dcf990fc885a464c0a1e1858e20d *\n!import 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db as N\n!export parseNames\n!export parsePrefix\n\n-- Lowercase names separated by commas. Try \"red,green\", \"red,,blue\",\n-- \"red,\" and \"red green\". A comma commits to another name.\n-- parseNames requires the entire input; parsePrefix exposes any remainder.\n-- Edit the parser or labels below; valid edits compile locally.\n-- Imports pin complete library interfaces, fetched only when compiling.\nnameParser = parserSatisfy (c : N.inRange c 97 122)\n  |> parserSome\n  |> parserLabel \"lowercase name\"\ncommaParser = parserThen (parserExpect 44) parserCommit\nnamesPrefix = parserSepBy1 nameParser commaParser\nnamesComplete = parserBefore namesPrefix <| parserLabel \"end of input\" parserEof\n\nreport reply = parserMatchReply\n  (diagnostic rest consumed committed : [\n    [\"status\" 0 \"error\"]\n    [\"expected\" 0 (parserDiagnosticExpected diagnostic)]\n    [\"remaining\" 0 rest]\n    [\"consumed\" 2 consumed]\n    [\"committed\" 2 committed]])\n  (names rest consumed committed : [\n    [\"status\" 0 \"ok\"]\n    [\"names\" 0 (intercalate \", \" names)]\n    [\"count\" 1 (length names)]\n    [\"remaining\" 0 rest]\n    [\"consumed\" 2 consumed]\n    [\"committed\" 2 committed]])\n  reply\nparseNames text = namesComplete text |> report\nparsePrefix text = namesPrefix text |> report\n",
    779     "sourceSha256": "9c005993cf43c07ec5c7e4f7f420b2f63c66e605d9522ab4b7e044de5e6cb8df",
    780     "export": "parseNames",
    781     "arguments": [
    782       {
    783         "type": "string",
    784         "value": "red,green,blue"
    785       }
    786     ],
    787     "output": "tricu",
    788     "mode": "pure",
    789     "presets": {
    790       "parseNames": {
    791         "arguments": [
    792           {
    793             "type": "string",
    794             "value": "red,green,blue"
    795           }
    796         ],
    797         "output": "tricu",
    798         "mode": "pure"
    799       },
    800       "parsePrefix": {
    801         "arguments": [
    802           {
    803             "type": "string",
    804             "value": "red,green,blue"
    805           }
    806         ],
    807         "output": "tricu",
    808         "mode": "pure"
    809       }
    810     }
    811   },
    812   {
    813     "id": "lexer",
    814     "preparation": "native",
    815     "environment": "empty",
    816     "category": "Language & Runtime Explorations",
    817     "title": "Located lexer scans",
    818     "hash": "e756a481b3056a2e76f50316ccf77bfab8b15727b7bec899580c2eb919e668b2",
    819     "source": "!import 6aa33a710955e5746f322775b9078f57ba4145f6b723ff4b76a292285e919823 *\n!import 14282088469cc420dd014700da06998f6bb8dcf990fc885a464c0a1e1858e20d *\n!import 816df1a32853750a1254112aff8ae8c55ca86bf70e809d226969ab1d43a6688b *\n!import 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db as N\n!export scanWord\n!export scanWhitespace\n\n-- Positioned prefix scans, not whole-input parsers. The remainder begins\n-- at the first nonmatching character. Positions count source characters,\n-- not UTF-8 bytes or display columns: only LF advances the line.\n-- Edit a predicate below and evaluate scanWord \"hello,world\" to compare.\n-- scanWhitespace accepts space, LF and tab, but deliberately not CR.\nreport reply = let\n  payload = parserReplyPayload reply\n  end = snd payload\n  in [\n    [\"matched\" 0 (fst payload)]\n    [\"remaining\" 0 (parserReplyRest reply)]\n    [\"line\" 1 (lexerPositionLine end)]\n    [\"column\" 1 (lexerPositionColumn end)]\n    [\"offset\" 1 (lexerPositionOffset end)]\n    [\"consumed\" 2 (parserReplyConsumed reply)]]\n\nscan predicate text = lexerScanWhile text lexerStart [] predicate (state character : pair character state)\n  |> lexerMapScan reverse\n  |> report\nscanWord text = scan (character : N.inRange character 97 122) text\nscanWhitespace text = scan (character : or? (equal? character 32)\n  (or? (equal? character 9) (equal? character 10))) text\n",
    820     "sourceSha256": "2a441d6d881acd6e34cd0caa8179ee1afefbce68c2e5ff1e44fb1768f69eeb3c",
    821     "export": "scanWord",
    822     "arguments": [
    823       {
    824         "type": "string",
    825         "value": "hello,world"
    826       }
    827     ],
    828     "output": "tricu",
    829     "mode": "pure",
    830     "presets": {
    831       "scanWord": {
    832         "arguments": [
    833           {
    834             "type": "string",
    835             "value": "hello,world"
    836           }
    837         ],
    838         "output": "tricu",
    839         "mode": "pure"
    840       },
    841       "scanWhitespace": {
    842         "arguments": [
    843           {
    844             "type": "string",
    845             "value": " \n\tword"
    846           }
    847         ],
    848         "output": "tricu",
    849         "mode": "pure"
    850       }
    851     }
    852   }
    853 ]