tricu

An interpreted language for exploring Tree Calculus
Log | Files | Refs | README | LICENSE

catalogue.json (38922B)


      1 [
      2   {
      3     "id": "names-values",
      4     "title": "Names and values",
      5     "category": "Basic Examples",
      6     "hash": "8d1b7e1f6dfefe983167ee8d9631476caaa0c3dc689a85628e9ca627ebaef6d0",
      7     "source": "!export greeting\n!export answer\n\n-- Give a value a name.\ngreeting = \"Hello, world!\"\n-- Evaluate greeting with Text.\n-- Result: Hello, world!\n-- Try changing the words, then Evaluate again.\n\n-- Numbers can have names too.\nanswer = 42\n-- Choose answer in Definition.\n-- It selects Natural number; the result is 42.\n\n-- Exercise: change answer to your favourite number and evaluate it.\n-- The !export lines make our names available to the playground.\n-- Edits compile after a pause; Evaluate runs the expression.\n-- Next: Doing arithmetic.\n",
      8     "sourceSha256": "77d8c61538436b86f620612a8502ba2980838b6e370cf71f6a364ff851517f3e",
      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     },
     25     "environment": "empty"
     26   },
     27   {
     28     "id": "arithmetic",
     29     "title": "Doing arithmetic",
     30     "category": "Basic Examples",
     31     "hash": "38110ab45a75a9c8771d6422277cb3083e667a4368aa33df0b43af672f17375e",
     32     "source": "!import 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db as Math\n!export *\n\n-- Add the prices of two items.\ntotal = Math.add 5 7\n-- Evaluate total with Natural number.\n-- Result: 12.\n-- Add import supplies the library identity; Math is our short name.\n\n-- Function calls use spaces: Math.multiply takes two numbers.\nproduct = Math.multiply 5 7\n\n-- Parentheses make one calculation the input to another.\ngrouped = Math.multiply (Math.add 5 7) 2\n\n-- Try product (35), then grouped (24), with Natural number.\n-- Exercise: change total to add the prices of two things you would buy.\n-- Numbers here are whole numbers starting at zero.\n-- Next: Your first function.\n",
     33     "sourceSha256": "67bfda56d700499e9f484f73d670c22156842fc963798127284be1f8482e2ba8",
     34     "export": "total",
     35     "arguments": [],
     36     "output": "number",
     37     "mode": "pure",
     38     "presets": {
     39       "total": {
     40         "arguments": [],
     41         "output": "number",
     42         "mode": "pure"
     43       },
     44       "product": {
     45         "arguments": [],
     46         "output": "number",
     47         "mode": "pure"
     48       },
     49       "grouped": {
     50         "arguments": [],
     51         "output": "number",
     52         "mode": "pure"
     53       }
     54     },
     55     "environment": "empty"
     56   },
     57   {
     58     "id": "functions",
     59     "title": "Your first function",
     60     "category": "Basic Examples",
     61     "hash": "be5f4675c9b0b6d9c093fe44a9695da9f428c88d62b0f4f1340017067fa81fa9",
     62     "source": "!import 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db as Math\n!export *\n\n-- Reuse a calculation with different inputs.\ndouble number = Math.multiply number 2\n-- Evaluate double 6 with Natural number.\n-- Result: 12.\n-- Add import supplies Math, including multiplication.\n\n-- number is a parameter: each call supplies its value.\n-- Try double 9: the same definition now returns 18.\n\n-- A function can also take more than one input.\nadd left right = Math.add left right\n-- Try add 4 7 with Natural number: the result is 11.\n\n-- Exercise: change double to multiply by 3, and give it the name triple.\n-- Then evaluate triple 6. Update the expression to use your new name.\n-- Next: Working with lists.\n",
     63     "sourceSha256": "44560b2f0d1df0ef24b853cc0e9a31a9a57108d8cfa56653a1840d264e498a24",
     64     "export": "double",
     65     "arguments": [
     66       {
     67         "type": "number",
     68         "value": "6"
     69       }
     70     ],
     71     "output": "number",
     72     "mode": "pure",
     73     "presets": {
     74       "double": {
     75         "arguments": [
     76           {
     77             "type": "number",
     78             "value": "6"
     79           }
     80         ],
     81         "output": "number",
     82         "mode": "pure"
     83       },
     84       "add": {
     85         "arguments": [
     86           {
     87             "type": "number",
     88             "value": "4"
     89           },
     90           {
     91             "type": "number",
     92             "value": "7"
     93           }
     94         ],
     95         "output": "number",
     96         "mode": "pure"
     97       }
     98     },
     99     "environment": "empty"
    100   },
    101   {
    102     "id": "lists",
    103     "title": "Working with lists",
    104     "category": "Basic Examples",
    105     "hash": "3cc5d36c5b70a20362117e3349aa044efca8adca56105424965065395a9f3e76",
    106     "source": "!import bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 as List\n!export count\n!export total\n\n-- Collect values in a list.\nnumbers = [2 4 6]\ncount = List.length numbers\ntotal = List.sum numbers\n\n-- Evaluate count with Natural number: the result is 3.\n-- Spaces separate items. [] is the empty list.\n-- Add import supplies List, including length and sum.\n\n-- Try total with Natural number: the result is 12.\n-- Exercise: add 8 to numbers. Check count (4) and total (20).\n-- Then try numbers = []: both results become 0.\n-- Next: Transforming a list.\n",
    107     "sourceSha256": "8d1dcc3840ccbfff3e2b64de18ba96ea81346b317dded4b7d3414657acf4fa7f",
    108     "export": "count",
    109     "arguments": [],
    110     "output": "number",
    111     "mode": "pure",
    112     "presets": {
    113       "count": {
    114         "arguments": [],
    115         "output": "number",
    116         "mode": "pure"
    117       },
    118       "total": {
    119         "arguments": [],
    120         "output": "number",
    121         "mode": "pure"
    122       }
    123     },
    124     "environment": "empty"
    125   },
    126   {
    127     "id": "transform-list",
    128     "title": "Transforming a list",
    129     "category": "Basic Examples",
    130     "hash": "9f10d772b42628acfe843eec7524d8de1ba9ea4344d3952c6c091c98c4d1c72d",
    131     "source": "!import bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 as List\n!import 7f87549d587fa8c3be88af0ea988db3adcc3b35f1ffb48973553491b075614db as Math\n!export total\n!export double\n\n-- Apply the same function to every item.\ndouble number = Math.multiply number 2\nnumbers = [2 4 6]\n\n-- map takes a function, then a list. Here it produces [4 8 12].\ndoubled = List.map double numbers\ntotal = List.sum doubled\n-- Evaluate total with Natural number: the result is 24.\n-- Add import supplies List and Math.\n\n-- Try double 5 with Natural number: the result is 10.\n-- Exercise: make double multiply by 3. The new total should be 36.\n-- Next: Making a choice.\n",
    132     "sourceSha256": "8e33d918846404760b398b473a93effad390b1486d5c2b053078170226cb8da9",
    133     "export": "total",
    134     "arguments": [],
    135     "output": "number",
    136     "mode": "pure",
    137     "presets": {
    138       "total": {
    139         "arguments": [],
    140         "output": "number",
    141         "mode": "pure"
    142       },
    143       "double": {
    144         "arguments": [
    145           {
    146             "type": "number",
    147             "value": "5"
    148           }
    149         ],
    150         "output": "number",
    151         "mode": "pure"
    152       }
    153     },
    154     "environment": "empty"
    155   },
    156   {
    157     "id": "choices",
    158     "title": "Making a choice",
    159     "category": "Basic Examples",
    160     "hash": "1972c98bfe599fef2a72cc946401a5cc9280026a8c46fdd7c8ccdc7093ee90cc",
    161     "source": "!export message\n\n-- Choose an answer by matching an input.\nmessage 0 = \"Your basket is empty.\"\nmessage 1 = \"You have one item.\"\nmessage _ = \"You have several items.\"\n\n-- Evaluate message 0 with Text: Your basket is empty.\n-- These equations are tried from top to bottom.\n-- _ matches any remaining input: this is the fallback.\n-- Try message 1, then message 4, using Text.\n\n-- Exercise: add a message for 2 items before the fallback equation.\n-- Keep the equations together so they define one function.\n-- Next: try a Practical Example, or browse Language reference.\n",
    162     "sourceSha256": "85ec05530278f77521c28f25437ff85b9418656eb7174a5ca7aba3abb826167f",
    163     "export": "message",
    164     "arguments": [
    165       {
    166         "type": "number",
    167         "value": "0"
    168       }
    169     ],
    170     "output": "string",
    171     "mode": "pure",
    172     "presets": {
    173       "message": {
    174         "arguments": [
    175           {
    176             "type": "number",
    177             "value": "0"
    178           }
    179         ],
    180         "output": "string",
    181         "mode": "pure"
    182       }
    183     },
    184     "environment": "empty"
    185   },
    186   {
    187     "id": "language-reference",
    188     "title": "Language reference",
    189     "category": "Language & Runtime Explorations",
    190     "hash": "86717d7f9d69fe730c6c6172c42c49895623aa070bf675e1b5f751adb30616c1",
    191     "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 / Natural number: 42. Choose a definition for its preset.\n-- Full contracts: follow For AI assistants in the footer to Language features.\n-- Each comment below gives an expression and its interpretation.\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 qualified 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 Add import 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",
    192     "sourceSha256": "5d4035e52ef2f9f85278d6431fc5be43df4e7badce7393930237fc1deb1c4fb8",
    193     "export": "answer",
    194     "arguments": [],
    195     "output": "number",
    196     "mode": "pure",
    197     "presets": {
    198       "answer": {
    199         "arguments": [],
    200         "output": "number",
    201         "mode": "pure"
    202       },
    203       "identity": {
    204         "arguments": [
    205           {
    206             "type": "number",
    207             "value": "7"
    208           }
    209         ],
    210         "output": "number",
    211         "mode": "pure"
    212       },
    213       "first": {
    214         "arguments": [
    215           {
    216             "type": "number",
    217             "value": "7"
    218           },
    219           {
    220             "type": "number",
    221             "value": "9"
    222           }
    223         ],
    224         "output": "number",
    225         "mode": "pure"
    226       },
    227       "leaf": {
    228         "arguments": [],
    229         "output": "tricu",
    230         "mode": "pure"
    231       },
    232       "shapes": {
    233         "arguments": [],
    234         "output": "tricu",
    235         "mode": "pure"
    236       },
    237       "literals": {
    238         "arguments": [],
    239         "output": "tricu",
    240         "mode": "pure"
    241       },
    242       "addTwo": {
    243         "arguments": [
    244           {
    245             "type": "number",
    246             "value": "5"
    247           }
    248         ],
    249         "output": "number",
    250         "mode": "pure"
    251       },
    252       "describe": {
    253         "arguments": [
    254           {
    255             "type": "number",
    256             "value": "7"
    257           }
    258         ],
    259         "output": "string",
    260         "mode": "pure"
    261       },
    262       "keep": {
    263         "arguments": [
    264           {
    265             "type": "tree",
    266             "value": "200"
    267           }
    268         ],
    269         "output": "tricu",
    270         "mode": "pure"
    271       },
    272       "local": {
    273         "arguments": [
    274           {
    275             "type": "number",
    276             "value": "6"
    277           }
    278         ],
    279         "output": "number",
    280         "mode": "pure"
    281       },
    282       "viaWhere": {
    283         "arguments": [
    284           {
    285             "type": "number",
    286             "value": "6"
    287           }
    288         ],
    289         "output": "number",
    290         "mode": "pure"
    291       },
    292       "copy": {
    293         "arguments": [
    294           {
    295             "type": "list",
    296             "value": [
    297               {
    298                 "type": "number",
    299                 "value": "1"
    300               },
    301               {
    302                 "type": "number",
    303                 "value": "2"
    304               },
    305               {
    306                 "type": "number",
    307                 "value": "3"
    308               }
    309             ]
    310           }
    311         ],
    312         "output": "bytes",
    313         "mode": "pure"
    314       },
    315       "firstOr": {
    316         "arguments": [
    317           {
    318             "type": "number",
    319             "value": "99"
    320           },
    321           {
    322             "type": "list",
    323             "value": [
    324               {
    325                 "type": "number",
    326                 "value": "5"
    327               }
    328             ]
    329           }
    330         ],
    331         "output": "number",
    332         "mode": "pure"
    333       },
    334       "localCopy": {
    335         "arguments": [
    336           {
    337             "type": "list",
    338             "value": [
    339               {
    340                 "type": "number",
    341                 "value": "4"
    342               },
    343               {
    344                 "type": "number",
    345                 "value": "5"
    346               }
    347             ]
    348           }
    349         ],
    350         "output": "bytes",
    351         "mode": "pure"
    352       },
    353       "checked": {
    354         "arguments": [
    355           {
    356             "type": "number",
    357             "value": "42"
    358           }
    359         ],
    360         "output": "number",
    361         "mode": "pure"
    362       },
    363       "sequenced": {
    364         "arguments": [
    365           {
    366             "type": "number",
    367             "value": "9"
    368           }
    369         ],
    370         "output": "number",
    371         "mode": "pure"
    372       }
    373     },
    374     "environment": "empty"
    375   },
    376   {
    377     "id": "greet",
    378     "title": "A conversation",
    379     "category": "Practical Examples",
    380     "hash": "0811457d501113ee11194f90e279ebb4ef9d548c70cb48d146be03eb9af7505f",
    381     "source": "!import 2d23be85d277457fd36c84f5eaa227e94e1c4efec281ff73017aaee109096f49 as IO\n!export main\n\n-- A conversation built from the IO library's actions and explicit bind operator.\n-- Evaluate main in Browser IO mode, then answer in the terminal input.\n-- The returned name is also displayed as Text. Try changing the greeting.\n-- Importing IO does not grant filesystem or network access in the browser.\nmain = (IO.io (do IO.bind\n  IO.putStr \"What is your name?\\n\"\n  name <- IO.getLine\n  IO.putStr \"Hello, \"\n  IO.putStr name\n  IO.putStr \"!\\n\"\n  IO.pure name))\n",
    382     "sourceSha256": "3b9835aec1f3b987ad954cd235a7c0517170d3f7e236b15d60ce9a3a809c74c4",
    383     "export": "main",
    384     "arguments": [],
    385     "output": "string",
    386     "mode": "io",
    387     "presets": {},
    388     "environment": "empty"
    389   },
    390   {
    391     "id": "microscope",
    392     "title": "Tree microscope",
    393     "category": "Language & Runtime Explorations",
    394     "hash": "493a125b649639c0df8348b4aeb3a455b7139cae0634ba114866c48813500bc5",
    395     "source": "!import bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 as B\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 = B.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.\ncount tree acc = (B.triage\n  (B.succ acc)\n  (child : count child (B.succ acc))\n  (left right : count right (count left (B.succ acc)))\n  tree)\n\nsize tree = count tree 0\n\nsame a b = (B.triage\n  (B.triage B.true (ignored : B.false) (ignored ignored2 : B.false) b)\n  (x : B.triage B.false (y : same x y) (ignored ignored2 : B.false) b)\n  (x y : B.triage B.false (ignored : B.false) (u v : B.and? (same x u) (same y v)) b)\n  a)\n",
    396     "sourceSha256": "0dd0bb5444662200cf3556dd7a81e65570b5d3b34d4b3e6b17e6d9c36d3d1114",
    397     "export": "size",
    398     "arguments": [
    399       {
    400         "type": "tree",
    401         "value": "21100"
    402       }
    403     ],
    404     "output": "number",
    405     "mode": "pure",
    406     "presets": {
    407       "shape": {
    408         "arguments": [
    409           {
    410             "type": "tree",
    411             "value": "21100"
    412           }
    413         ],
    414         "output": "string",
    415         "mode": "pure"
    416       },
    417       "size": {
    418         "arguments": [
    419           {
    420             "type": "tree",
    421             "value": "21100"
    422           }
    423         ],
    424         "output": "number",
    425         "mode": "pure"
    426       },
    427       "same": {
    428         "arguments": [
    429           {
    430             "type": "tree",
    431             "value": "21100"
    432           },
    433           {
    434             "type": "tree",
    435             "value": "21100"
    436           }
    437         ],
    438         "output": "bool",
    439         "mode": "pure"
    440       }
    441     },
    442     "environment": "empty"
    443   },
    444   {
    445     "id": "rgb",
    446     "title": "RGB validator",
    447     "category": "Practical Examples",
    448     "hash": "b8c0e21c09f63511794cc62827a6b231e62a55f7e779022a1b740aaba29b0368",
    449     "source": "!import bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 *\n!import 7a24af56a9ce9b947cadcafd3a9da127b1507275bfac62b9fa55e27490b176dc as Binary\n!export main\n\n-- Validate an RGB list using the library's canonical-byte predicate.\n-- Try main [64 128 255], main [0 256 255], or main [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? (Binary.binaryByte? r) (and? (Binary.binaryByte? g) (Binary.binaryByte? b)) of\n  ^true = \"RGB triple accepted.\"\n  _ = \"A channel is not a canonical byte (0-255).\"\nmain _ = \"Expected exactly three channels.\"\n",
    450     "sourceSha256": "33da7deb793df280f59b47913b57562dbb2820525f19451ba5e4042219ef559e",
    451     "export": "main",
    452     "arguments": [
    453       {
    454         "type": "bytes",
    455         "value": [
    456           64,
    457           128,
    458           255
    459         ]
    460       }
    461     ],
    462     "output": "string",
    463     "mode": "pure",
    464     "presets": {},
    465     "environment": "empty"
    466   },
    467   {
    468     "id": "shipping",
    469     "title": "Shipping policies",
    470     "category": "Practical Examples",
    471     "hash": "4aa9ce9f19dfce87ecb3f6bd0a2e881dafcbcd3d2e4b4075befbb378d80833ea",
    472     "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-- Select v1/v2 with the same cart: 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",
    473     "sourceSha256": "d6d9378ab39733a8aabaf4f6a25851350e99fa81607bf05a64266301e2712f5a",
    474     "export": "v1",
    475     "arguments": [
    476       {
    477         "type": "string",
    478         "value": "domestic"
    479       },
    480       {
    481         "type": "number",
    482         "value": "8000"
    483       },
    484       {
    485         "type": "number",
    486         "value": "1500"
    487       }
    488     ],
    489     "output": "record",
    490     "mode": "pure",
    491     "presets": {
    492       "v1": {
    493         "arguments": [
    494           {
    495             "type": "string",
    496             "value": "domestic"
    497           },
    498           {
    499             "type": "number",
    500             "value": "8000"
    501           },
    502           {
    503             "type": "number",
    504             "value": "1500"
    505           }
    506         ],
    507         "output": "record",
    508         "mode": "pure"
    509       },
    510       "v2": {
    511         "arguments": [
    512           {
    513             "type": "string",
    514             "value": "domestic"
    515           },
    516           {
    517             "type": "number",
    518             "value": "8000"
    519           },
    520           {
    521             "type": "number",
    522             "value": "1500"
    523           }
    524         ],
    525         "output": "record",
    526         "mode": "pure"
    527       }
    528     },
    529     "environment": "empty"
    530   },
    531   {
    532     "id": "events",
    533     "title": "Event adapters",
    534     "category": "Practical Examples",
    535     "hash": "8b51693c63658d59c8083ec1c95cf1f6565ce5799791f540e92caf508613c9fb",
    536     "source": "!import bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 *\n!import 3f70d95cf1bae0928ee89acd2a9e0cdd8287796efae32a9f08d33e3dd0149c55 as A\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 the two exports: their presets describe the same order.created 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; A.hostString? checks Unicode scalar text.\n\nfailure message = [[\"status\" 0 \"error\"] [\"message\" 0 message]]\n\nnormalized event customer cents = case A.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",
    537     "sourceSha256": "ca11d9a7e20bfe228545c43f50aed57089c020eead516e23b67d909fae8923de",
    538     "export": "vendorA",
    539     "arguments": [
    540       {
    541         "type": "list",
    542         "value": [
    543           {
    544             "type": "string",
    545             "value": "purchase"
    546           },
    547           {
    548             "type": "string",
    549             "value": "James"
    550           },
    551           {
    552             "type": "number",
    553             "value": "2499"
    554           }
    555         ]
    556       }
    557     ],
    558     "output": "record",
    559     "mode": "pure",
    560     "presets": {
    561       "vendorA": {
    562         "arguments": [
    563           {
    564             "type": "list",
    565             "value": [
    566               {
    567                 "type": "string",
    568                 "value": "purchase"
    569               },
    570               {
    571                 "type": "string",
    572                 "value": "James"
    573               },
    574               {
    575                 "type": "number",
    576                 "value": "2499"
    577               }
    578             ]
    579           }
    580         ],
    581         "output": "record",
    582         "mode": "pure"
    583       },
    584       "vendorB": {
    585         "arguments": [
    586           {
    587             "type": "list",
    588             "value": [
    589               {
    590                 "type": "string",
    591                 "value": "James"
    592               },
    593               {
    594                 "type": "bool",
    595                 "value": false
    596               },
    597               {
    598                 "type": "number",
    599                 "value": "2499"
    600               }
    601             ]
    602           }
    603         ],
    604         "output": "record",
    605         "mode": "pure"
    606       }
    607     },
    608     "environment": "empty"
    609   },
    610   {
    611     "id": "registration",
    612     "title": "Registration validator",
    613     "category": "Practical Examples",
    614     "hash": "679733e17a86cc5e3f5282ca824cad4c9c0febd260e51f6a87e16dd14c02c45a",
    615     "source": "!import bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 *\n!import 3f70d95cf1bae0928ee89acd2a9e0cdd8287796efae32a9f08d33e3dd0149c55 as A\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-- 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; A.hostString? checks Unicode scalar text.\n\nnameMessage name = case A.hostString? name of\n  ^true = (case name of\n    [] = \"Name is required.\"\n    _ = \"\")\n  _ = \"Name must be scalar text.\"\n\nemailMessage email = case A.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",
    616     "sourceSha256": "531866c6b2e7e42ecbd4ae285c502b59321ad31fd2b82b36d8a139938592ac31",
    617     "export": "main",
    618     "arguments": [
    619       {
    620         "type": "string",
    621         "value": "James"
    622       },
    623       {
    624         "type": "string",
    625         "value": "james@example.org"
    626       },
    627       {
    628         "type": "number",
    629         "value": "21"
    630       },
    631       {
    632         "type": "bool",
    633         "value": true
    634       }
    635     ],
    636     "output": "record",
    637     "mode": "pure",
    638     "presets": {
    639       "main": {
    640         "arguments": [
    641           {
    642             "type": "string",
    643             "value": "James"
    644           },
    645           {
    646             "type": "string",
    647             "value": "james@example.org"
    648           },
    649           {
    650             "type": "number",
    651             "value": "21"
    652           },
    653           {
    654             "type": "bool",
    655             "value": true
    656           }
    657         ],
    658         "output": "record",
    659         "mode": "pure"
    660       }
    661     },
    662     "environment": "empty"
    663   },
    664   {
    665     "id": "migration",
    666     "title": "Profile migrations",
    667     "category": "Practical Examples",
    668     "hash": "730aeb8203ff504b7483999a6477a292b10871f82e94a77d3d5f7b49ecc3b6b3",
    669     "source": "!import bd2c14d6db6b69308a5252a8e839705a55d30e815f012900b094315263e08d81 *\n!import 3f70d95cf1bae0928ee89acd2a9e0cdd8287796efae32a9f08d33e3dd0149c55 as A\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-- Select v1/v2 with the same input 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; A.hostString? checks Unicode scalar text.\n\nfailure message = [[\"status\" 0 \"error\"] [\"message\" 0 message]]\n\nmigrate version name email = case and? (A.hostString? name) (A.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",
    670     "sourceSha256": "66ee7bc1a9005b5f1d12f168b7866231ad7f523cf34f52b63958cb155a5c49b5",
    671     "export": "v1",
    672     "arguments": [
    673       {
    674         "type": "list",
    675         "value": [
    676           {
    677             "type": "string",
    678             "value": "James"
    679           },
    680           {
    681             "type": "string",
    682             "value": "james@example.org"
    683           }
    684         ]
    685       }
    686     ],
    687     "output": "record",
    688     "mode": "pure",
    689     "presets": {
    690       "v1": {
    691         "arguments": [
    692           {
    693             "type": "list",
    694             "value": [
    695               {
    696                 "type": "string",
    697                 "value": "James"
    698               },
    699               {
    700                 "type": "string",
    701                 "value": "james@example.org"
    702               }
    703             ]
    704           }
    705         ],
    706         "output": "record",
    707         "mode": "pure"
    708       },
    709       "v2": {
    710         "arguments": [
    711           {
    712             "type": "list",
    713             "value": [
    714               {
    715                 "type": "string",
    716                 "value": "James"
    717               },
    718               {
    719                 "type": "string",
    720                 "value": "james@example.org"
    721               }
    722             ]
    723           }
    724         ],
    725         "output": "record",
    726         "mode": "pure"
    727       }
    728     },
    729     "environment": "empty"
    730   },
    731   {
    732     "id": "parser",
    733     "preparation": "native",
    734     "environment": "empty",
    735     "title": "Parser combinators",
    736     "hash": "355e41506f39e05da8c8a72a68aa810a486e062480322bac11af67e86980520e",
    737     "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 = parserLabel \"lowercase name\" (parserSome (parserSatisfy (c : N.inRange c 97 122)))\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 = report (namesComplete text)\nparsePrefix text = report (namesPrefix text)\n",
    738     "sourceSha256": "18aa0a464dabb595749abdab5edb1bb11c4356c0e753cf0985a644901bb5c241",
    739     "export": "parseNames",
    740     "arguments": [
    741       {
    742         "type": "string",
    743         "value": "red,green,blue"
    744       }
    745     ],
    746     "output": "record",
    747     "mode": "pure",
    748     "presets": {
    749       "parseNames": {
    750         "arguments": [
    751           {
    752             "type": "string",
    753             "value": "red,green,blue"
    754           }
    755         ],
    756         "output": "record",
    757         "mode": "pure"
    758       },
    759       "parsePrefix": {
    760         "arguments": [
    761           {
    762             "type": "string",
    763             "value": "red,green,blue"
    764           }
    765         ],
    766         "output": "record",
    767         "mode": "pure"
    768       }
    769     },
    770     "category": "Language & Runtime Explorations"
    771   },
    772   {
    773     "id": "lexer",
    774     "preparation": "native",
    775     "environment": "empty",
    776     "title": "Located lexer scans",
    777     "hash": "e756a481b3056a2e76f50316ccf77bfab8b15727b7bec899580c2eb919e668b2",
    778     "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 = report (lexerMapScan reverse\n  (lexerScanWhile text lexerStart [] predicate (state character : pair character state)))\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",
    779     "sourceSha256": "ec68af4102c16ef56be01e78a3288bea57af71ca57ab35374a0d12005b5a10a1",
    780     "export": "scanWord",
    781     "arguments": [
    782       {
    783         "type": "string",
    784         "value": "hello,world"
    785       }
    786     ],
    787     "output": "record",
    788     "mode": "pure",
    789     "presets": {
    790       "scanWord": {
    791         "arguments": [
    792           {
    793             "type": "string",
    794             "value": "hello,world"
    795           }
    796         ],
    797         "output": "record",
    798         "mode": "pure"
    799       },
    800       "scanWhitespace": {
    801         "arguments": [
    802           {
    803             "type": "string",
    804             "value": " \n\tword"
    805           }
    806         ],
    807         "output": "record",
    808         "mode": "pure"
    809       }
    810     },
    811     "category": "Language & Runtime Explorations"
    812   }
    813 ]