tricu

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

97d703664739df908db963ab2f722b62ea14fa7175351e8ad788c97c5ec20621.tri (22024B)


      1 !export *
      2 -- 1.  t  t      a b       -> a
      3 -- 2.  t (t a)   b c       -> a c (b c)
      4 -- 3a. t (t a b) c t       -> a
      5 -- 3b. t (t a b) c (t u)   -> b u
      6 -- 3c. t (t a b) c (t u v) -> c u v
      7 
      8 false = t
      9 _     = t
     10 true  = t t
     11 id a = a
     12 const a b = a
     13 pair  = t
     14 if cond then else = t (t else (t t then)) t cond
     15 
     16 y = ((mut wait fun : wait mut (x : fun (wait mut x)))
     17      (x : x x)
     18      (a0 a1 a2 : t (t a0) (t t a2) a1))
     19 
     20 compose f g x = f (g x)
     21 
     22 triage leaf stem fork = t (t leaf stem) fork
     23 test   = triage "Leaf" (_ : "Stem") (_ _ : "Fork")
     24 
     25 matchBool = (ot of : triage
     26   of
     27   (_ : ot)
     28   (_ _ : ot)
     29 )
     30 
     31 lAnd = (triage
     32   (_     : false)
     33   (_ x   : x)
     34   (_ _ x : x))
     35 
     36 lOr = (triage
     37   (x     : x)
     38   (_ _   : true)
     39   (_ _ _ : true))
     40 
     41 matchPair a = triage _ _ a
     42 
     43 fst p = matchPair takeFirst p
     44   where takeFirst a b = a
     45 snd p = matchPair takeSecond p
     46   where takeSecond a b = b
     47 
     48 resultIsOk result =
     49   matchResult (errR rest : false) (val rest : true) result
     50 
     51 resultIsErr result =
     52   matchResult (errR rest : true) (val rest : false) result
     53 
     54 not? = matchBool false true
     55 and? = matchBool id (_ : false)
     56 
     57 or? = (x z : 
     58   matchBool 
     59     (matchBool true true z)
     60     (matchBool true false z)
     61     x)
     62 
     63 xor? = (x z : 
     64   matchBool 
     65     (matchBool false true z)
     66     (matchBool true false z)
     67     x)
     68 
     69 equal? = y (self : triage
     70   (triage
     71     true
     72     (_   : false)
     73     (_ _ : false))
     74   (ax :
     75     triage
     76       false
     77       (self ax)
     78       (_ _ : false))
     79   (ax ay :
     80     triage
     81       false
     82       (_ : false)
     83       (bx by : lAnd (self ax bx) (self ay by))))
     84 
     85 succ = y (self :
     86   triage
     87     1
     88     t
     89     (triage
     90       (t (t t))
     91       (_ tail : t t (self tail))
     92       t))
     93 
     94 ok value rest = pair true (pair value rest)
     95 err msg rest = pair false (pair msg rest)
     96 
     97 matchResult errCase okCase result =
     98   matchPair
     99     (tag payload :
    100       matchPair
    101         (value rest :
    102           matchBool
    103             (okCase value rest)
    104             (errCase value rest)
    105             tag)
    106         payload)
    107     result
    108 
    109 -- ---------------------------------------------------------------------------
    110 -- Maybe / Option type
    111 -- ---------------------------------------------------------------------------
    112 
    113 nothing = t
    114 just x = t x
    115 
    116 matchMaybe nothingCase justCase maybe =
    117   triage
    118     nothingCase
    119     justCase
    120     (_ _ : nothingCase)
    121     maybe
    122 
    123 maybe default f m = matchMaybe default f m
    124 maybeMap f m = matchMaybe nothing (x : just (f x)) m
    125 maybeBind m f = matchMaybe nothing f m
    126 maybeOr default m = matchMaybe default id m
    127 maybe? = matchMaybe false (_ : true)
    128 
    129 -- ---------------------------------------------------------------------------
    130 -- Lazy eliminators
    131 --
    132 -- A strict eliminator evaluates both branches because they are ordinary
    133 -- arguments. Give a branch that recurses, looks something up, or builds
    134 -- structure to one of these instead: it becomes a thunk and only the selected
    135 -- branch is ever applied.
    136 -- ---------------------------------------------------------------------------
    137 
    138 lazyBool = (thenK elseK cond :
    139   ((chosen : chosen t)
    140    (matchBool
    141      thenK
    142      elseK
    143      cond)))
    144 
    145 -- `triage` selects a thunk directly: a cons is a Fork, so the cons case sits
    146 -- in the fork slot, exactly as in `matchList` below.
    147 lazyList = (nilK consK xs :
    148   ((chosen : chosen t)
    149    (triage
    150      nilK
    151      _
    152      (h r : (_ : consK h r))
    153      xs)))
    154 
    155 lazyMaybe = (noneK someK m :
    156   ((chosen : chosen t)
    157    (matchMaybe
    158      noneK
    159      (x : (_ : someK x))
    160      m)))
    161 
    162 lazyResult = (errK okK result :
    163   ((chosen : chosen t)
    164    (matchResult
    165      (code rest : (_ : errK code rest))
    166      (value rest : (_ : okK value rest))
    167      result)))
    168 
    169 -- ---------------------------------------------------------------------------
    170 -- Basic arithmetic
    171 -- ---------------------------------------------------------------------------
    172 
    173 ifLazy = (cond thenK elseK :
    174   matchBool
    175     (thenK t)
    176     (elseK t)
    177     cond)
    178 
    179 andLazy? = (a bK :
    180   ifLazy
    181     a
    182     bK
    183     (_ : false))
    184 
    185 pred_ = y (self : triage
    186   0
    187   0
    188   (bit rest :
    189     ifLazy
    190       bit
    191       (_ : matchBool
    192         (t t rest)
    193         0
    194         rest)
    195       (_ : t (t t) (self rest))))
    196 
    197 pred @nat? =@nat? pred_
    198 
    199 isZero_? = triage true (_ : false) (_ _ : false)
    200 
    201 isZero? @nat? =@bool? isZero_?
    202 
    203 add @nat? @nat? =@nat? (y (self x y :
    204   triage
    205     y
    206     (_ : succ y)
    207     (_ _ : succ (self (pred_ x) y))
    208     x))
    209 
    210 sub @nat? @nat? =@nat? y (self a b :
    211   ifLazy
    212     (isZero_? b)
    213     (_ : a)
    214     (_ : self (pred_ a) (pred_ b)))
    215 
    216 lte_? = y (self a b :
    217   ifLazy
    218     (isZero_? a)
    219     (_ : true)
    220     (_ :
    221       ifLazy
    222         (isZero_? b)
    223         (_ : false)
    224         (_ : self (pred_ a) (pred_ b))))
    225 
    226 lte? @nat? @nat? =@bool? lte_?
    227 
    228 gte_? = a b : lte_? b a
    229 
    230 gte? @nat? @nat? =@bool? gte_?
    231 
    232 lt_? = a b : and? (lte_? a b) (not? (equal? a b))
    233 
    234 lt? @nat? @nat? =@bool? lt_?
    235 
    236 gt_? = a b : lt_? b a
    237 
    238 gt? @nat? @nat? =@bool? gt_?
    239 
    240 mul @nat? @nat? =@nat? y (self a b :
    241   ifLazy
    242     (isZero_? b)
    243     (_ : 0)
    244     (_ : add a (self a (pred_ b))))
    245 
    246 div @nat? @nat? =@nat? y (self a b :
    247   ifLazy
    248     (isZero_? b)
    249     (_ : 0)
    250     (_ : ifLazy
    251       (lt_? a b)
    252       (_ : 0)
    253       (_ : succ (self (sub a b) b))))
    254 
    255 mod @nat? @nat? =@nat? y (self a b :
    256   ifLazy
    257     (isZero_? b)
    258     (_ : 0)
    259     (_ : ifLazy
    260       (lt_? a b)
    261       (_ : a)
    262       (_ : self (sub a b) b)))
    263 
    264 pow @nat? @nat? =@nat? y (self a b :
    265   ifLazy
    266     (isZero_? b)
    267     (_ : 1)
    268     (_ : mul a (self a (pred_ b))))
    269 
    270 even? n = (triage
    271   true
    272   (_ : false)
    273   (bit _ : isZero_? bit)
    274   n)
    275 
    276 odd? = (n : not? (even? n))
    277 
    278 min @nat? @nat? =@nat? (a b : ifLazy (lte_? a b) (_ : a) (_ : b))
    279 
    280 max @nat? @nat? =@nat? (a b : ifLazy (lte_? a b) (_ : b) (_ : a))
    281 
    282 -- ---------------------------------------------------------------------------
    283 -- Result combinators
    284 -- ---------------------------------------------------------------------------
    285 
    286 mapResult = (f result :
    287   matchResult
    288     (code rest : err code rest)
    289     (value rest : ok (f value) rest)
    290     result)
    291 
    292 bindResult = (result f :
    293   matchResult
    294     (code rest : err code rest)
    295     (value rest : f value rest)
    296     result)
    297 
    298 resultOr = (default result :
    299   matchResult
    300     (_ _ : default)
    301     (value _ : value)
    302     result)
    303 
    304 resultMapErr = (f result :
    305   matchResult
    306     (code rest : err (f code) rest)
    307     (value rest : ok value rest)
    308     result)
    309 
    310 -- ---------------------------------------------------------------------------
    311 -- List
    312 -- ---------------------------------------------------------------------------
    313 
    314 matchList = a b : triage a _ b
    315 
    316 emptyList? = matchList true (_ _ : false)
    317 head xs@(nonEmptyListOf anyC) =@anyC matchList t (h _ : h) xs
    318 tail xs@(nonEmptyListOf anyC) =@(listOf anyC) matchList t (_ r : r) xs
    319 
    320 append_ self xs ys =
    321   matchList
    322     ys
    323     (h r : pair h (self r ys))
    324     xs
    325 append = xs ys : y append_ xs ys
    326 
    327 lExist?_ self x xs =
    328   matchList
    329     false
    330     (h r : or? (equal? x h) (self x r))
    331     xs
    332 lExist? = x xs : y lExist?_ x xs
    333 
    334 map_ self l f =
    335   matchList
    336     t
    337     (h r : pair (f h) (self r f))
    338     l
    339 map = f l : y map_ l f
    340 
    341 filter_ self l f =
    342   matchList
    343     t
    344     (h r :
    345       matchBool
    346         (pair h (self r f))
    347         (self r f)
    348         (f h))
    349     l
    350 filter = f l : y filter_ l f
    351 
    352 foldl_ self l f acc =
    353   matchList
    354     acc
    355     (h r : self r f (f acc h))
    356     l
    357 foldl = f x l : y foldl_ l f x
    358 
    359 foldr_ self l f x =
    360   matchList
    361     x
    362     (h r : f (self r f x) h)
    363     l
    364 foldr = f x l : y foldr_ l f x
    365 
    366 length_ self xs =
    367   matchList
    368     0
    369     (_ r : succ (self r))
    370     xs
    371 length @(listOf anyC) =@nat? y length_
    372 
    373 reverse_ self xs acc =
    374   matchList
    375     acc
    376     (h r : self r (pair h acc))
    377     xs
    378 reverse = xs : y reverse_ xs t
    379 
    380 snoc_ self x xs =
    381   matchList
    382     (pair x t)
    383     (h r : pair h (self x r))
    384     xs
    385 snoc = x xs : y snoc_ x xs
    386 
    387 count_ self x xs =
    388   matchList
    389     0
    390     (h r :
    391       matchBool
    392         (succ (self x r))
    393         (self x r)
    394         (equal? x h))
    395     xs
    396 count = x xs : y count_ x xs
    397 
    398 last_ self xs =
    399   matchList
    400     t
    401     (h r :
    402       matchBool
    403         h
    404         (self r)
    405         (emptyList? r))
    406     xs
    407 last @(nonEmptyListOf anyC) =@anyC y last_
    408 
    409 all?_ self pred xs =
    410   matchList
    411     true
    412     (h r : and? (pred h) (self pred r))
    413     xs
    414 all? = pred xs : y all?_ pred xs
    415 
    416 any?_ self pred xs =
    417   matchList
    418     false
    419     (h r : or? (pred h) (self pred r))
    420     xs
    421 any? = pred xs : y any?_ pred xs
    422 
    423 intersect = xs ys : filter (x : lExist? x ys) xs
    424 
    425 nth_ self xs n i =
    426   matchList
    427     t
    428     (h r :
    429       matchBool
    430         h
    431         (self r n (succ i))
    432         (equal? i n))
    433     xs
    434 nth = n xs : y nth_ xs n 0
    435 
    436 headMaybe = matchList nothing (h _ : just h)
    437 
    438 lastMaybe_ self xs =
    439   matchList
    440     nothing
    441     (h r :
    442       matchBool
    443         (just h)
    444         (self r)
    445         (emptyList? r))
    446     xs
    447 lastMaybe = xs : y lastMaybe_ xs
    448 
    449 nthMaybe_ self xs n i =
    450   matchList
    451     nothing
    452     (h r :
    453       matchBool
    454         (just h)
    455         (self r n (succ i))
    456         (equal? i n))
    457     xs
    458 nthMaybe = n xs : y nthMaybe_ xs n 0
    459 
    460 take_ self xs n i =
    461   matchList
    462     t
    463     (h r :
    464       matchBool
    465         t
    466         (pair h (self r n (succ i)))
    467         (equal? i n))
    468     xs
    469 take = n xs : y take_ xs n 0
    470 
    471 drop_ self xs n i =
    472   matchBool
    473     xs
    474     (matchList
    475       t
    476       (_ r : self r n (succ i))
    477       xs)
    478     (equal? i n)
    479 drop = n xs : y drop_ xs n 0
    480 
    481 splitAt = n xs : pair (take n xs) (drop n xs)
    482 
    483 concatMap_ self f xs =
    484   matchList
    485     t
    486     (h r : append (f h) (self f r))
    487     xs
    488 concatMap = f xs : y concatMap_ f xs
    489 
    490 find_ self pred xs =
    491   matchList
    492     nothing
    493     (h r :
    494       matchBool
    495         (just h)
    496         (self pred r)
    497         (pred h))
    498     xs
    499 find = pred xs : y find_ pred xs
    500 
    501 partition_ self pred xs trues falses =
    502   matchList
    503     (pair (reverse trues) (reverse falses))
    504     (h r :
    505       matchBool
    506         (self pred r (pair h trues) falses)
    507         (self pred r trues (pair h falses))
    508         (pred h))
    509     xs
    510 partition = pred xs : y partition_ pred xs t t
    511 
    512 strLength = length
    513 strAppend = append
    514 strEq? = equal?
    515 strEmpty? = emptyList?
    516 
    517 startsWith?_ self prefix input =
    518   matchList
    519     true
    520     (ph pr :
    521       matchList
    522         false
    523         (sh sr :
    524           matchBool
    525             (self pr sr)
    526             false
    527             (equal? ph sh))
    528         input)
    529     prefix
    530 startsWith? = prefix input : y startsWith?_ prefix input
    531 
    532 endsWith? = prefix str : startsWith? (reverse prefix) (reverse str)
    533 
    534 contains?_ self needle haystack =
    535   matchBool
    536     true
    537     (matchList
    538       false
    539       (_ r : self needle r)
    540       haystack)
    541     (startsWith? needle haystack)
    542 contains? = needle haystack : y contains?_ needle haystack
    543 
    544 sum @(listOf nat?) =@nat? foldl (acc x : add x acc) 0
    545 product @(listOf nat?) =@nat? foldl (acc x : mul x acc) 1
    546 
    547 -- ---------------------------------------------------------------------------
    548 -- Generic separators
    549 --
    550 -- `lines`, `unlines`, `words` and `unwords` at the bottom of this section are
    551 -- the byte-valued special cases of these primitives.
    552 --
    553 -- Joining takes any separator; splitting takes one byte. Separators are removed
    554 -- rather than kept, and empty fields are preserved.
    555 -- ---------------------------------------------------------------------------
    556 
    557 takeWhile_ self xs f =
    558   lazyList
    559     (_ : t)
    560     (h r :
    561       lazyBool
    562         (_ : pair h (self r f))
    563         (_ : t)
    564         (f h))
    565     xs
    566 takeWhile = f xs : y takeWhile_ xs f
    567 
    568 dropWhile_ self xs f =
    569   lazyList
    570     (_ : t)
    571     (h r :
    572       lazyBool
    573         (_ : self r f)
    574         (_ : pair h r)
    575         (f h))
    576     xs
    577 dropWhile = f xs : y dropWhile_ xs f
    578 
    579 -- Byte-level whitespace only: space and horizontal tab (HTTP OWS).
    580 spaceByte? = b : equal? b 32
    581 tabByte?   = b : equal? b 9
    582 trimByte?  = b : or? (spaceByte? b) (tabByte? b)
    583 
    584 trim = xs : dropWhile trimByte? (reverse (dropWhile trimByte? (reverse xs)))
    585 
    586 intercalate_ self xs sep =
    587   lazyList
    588     (_ : t)
    589     (h r :
    590       lazyBool
    591         (_ : h)
    592         (_ : append h (append sep (self r sep)))
    593         (emptyList? r))
    594     xs
    595 intercalate = sep xs : y intercalate_ xs sep
    596 
    597 -- Separator after every field, including the last one. Line-oriented formats
    598 -- want this: `joinSuffix "\n" xs` terminates the final line while
    599 -- `intercalate "\n" xs` does not.
    600 joinSuffix_ self xs sep =
    601   lazyList
    602     (_ : t)
    603     (h r : append (append h sep) (self r sep))
    604     xs
    605 joinSuffix = sep xs : y joinSuffix_ xs sep
    606 
    607 -- Split on a single byte. 
    608 -- Empty fields are preserved: `splitOnByte 58 "a::b"` is ["a" "" "b"].
    609 splitByte_ self str byte acc current =
    610   lazyList
    611     (_ : map reverse (reverse (pair current acc)))
    612     (h r :
    613       lazyBool
    614         (_ : self r byte (pair current acc) t)
    615         (_ : self r byte acc (pair h current))
    616         (equal? h byte))
    617     str
    618 splitOnByte = byte str : y splitByte_ str byte t t
    619 
    620 -- Every one of these keeps its arguments bound: partially applying a
    621 -- multi-argument function at the top level leaves a fixed point exposed.
    622 lines   = str : splitOnByte 10 str
    623 unlines = xs : joinSuffix "\n" xs
    624 
    625 -- Runs of separators collapse: empty fields are dropped.
    626 words   = str : filter (w : not? (emptyList? w)) (splitOnByte 32 str)
    627 unwords = xs : intercalate " " xs
    628 
    629 zipWith_ self f xs ys =
    630   matchList
    631     t
    632     (xh xt :
    633       matchList
    634         t
    635         (yh yt : pair (f xh yh) (self f xt yt))
    636         ys)
    637     xs
    638 zipWith = f xs ys : y zipWith_ f xs ys
    639 
    640 -- ---------------------------------------------------------------------------
    641 -- Core contract type
    642 --
    643 -- A contract is an ordinary tricu function: Tree -> Tree -> Result Tree Tree.
    644 -- The second argument is the conventional "rest" slot. On success a contract
    645 -- returns the checked value wrapped in the standard ok shape; on failure it
    646 -- returns a diagnostic wrapped in the standard err shape.
    647 --
    648 -- The contract kernel is a globally configurable function selected by the
    649 -- runner. It decides whether to accept the contract result, replace it, log
    650 -- it, or transform the diagnostic. The default kernel is the identity on the
    651 -- contract Result.
    652 --
    653 -- The runner may rebind 'kernel' to a different kernel before evaluating
    654 -- user code (e.g. via --contract-kernel).
    655 -- ---------------------------------------------------------------------------
    656 
    657 contractOk = (value : (rest : ok value rest))
    658 contractErr = (msg : (rest : err msg rest))
    659 
    660 -- Default contract kernel. Return the contract Result unchanged.
    661 defaultKernel = (contract value result :
    662   matchResult
    663     (msg rest : err msg rest)
    664     (v rest : ok v rest)
    665     result)
    666 
    667 -- The active kernel. The runner may rebind this name to a different kernel
    668 -- before evaluating user code (e.g. via --contract-kernel). Internally,
    669 -- withContract dispatches through this binding, so rebinding 'kernel' changes
    670 -- the behaviour of every contract boundary in the program.
    671 kernel = defaultKernel
    672 
    673 -- Skip-everything kernel. Resume with the original value on failure.
    674 skipKernel = (contract value result :
    675   matchResult
    676     (msg rest : ok value rest)
    677     (v rest : ok v rest)
    678     result)
    679 
    680 -- Apply a contract and pass the raw Result to the kernel.
    681 withContract = (contract value :
    682   kernel contract value (contract value t))
    683 
    684 -- Apply a contract and return the checked value or the diagnostic message.
    685 check contract value =
    686   matchResult
    687     (msg _ : msg)
    688     (v _ : v)
    689     (withContract contract value)
    690 
    691 -- Apply a contract and return the raw Result (kernel is bypassed).
    692 checkContract = (contract value : contract value t)
    693 
    694 -- ---------------------------------------------------------------------------
    695 -- Basic contracts
    696 -- ---------------------------------------------------------------------------
    697 
    698 -- Any value passes.
    699 anyC = (value : contractOk value)
    700 
    701 -- Always fails with the supplied message.
    702 neverC = (msg : (value : contractErr msg))
    703 
    704 -- Build a contract from a predicate that inspects only the value.
    705 guardC = (msg predicate value rest :
    706   lazyBool
    707     (_ : contractOk value rest)
    708     (_ : contractErr msg rest)
    709     (predicate value))
    710 
    711 -- Structural natural-number predicate.
    712 -- A natural is either Leaf (0) or Fork bit rest where bit is Leaf (even)
    713 -- or Stem Leaf (odd) and rest is itself a natural.
    714 isNat? = y (self n :
    715   triage
    716     true
    717     (_ : false)
    718     (bit r :
    719       triage
    720         (self r)
    721         (_ : self r)
    722         (_ _ : false)
    723         bit)
    724     n)
    725 
    726 -- Natural number contract.
    727 nat? = guardC "not a natural number" isNat?
    728 
    729 -- Non-zero natural number contract.
    730 nonZero? = guardC "non-zero" (n : and? (isNat? n) (not? (isZero_? n)))
    731 
    732 -- Boolean contract.
    733 bool? = guardC "not a boolean" (b : or? (equal? b true) (equal? b false))
    734 
    735 -- ---------------------------------------------------------------------------
    736 -- Contract combinators
    737 -- ---------------------------------------------------------------------------
    738 
    739 andC = (c1 c2 value rest :
    740   matchResult
    741     (msg _ : contractErr msg rest)
    742     (v _ : c2 v rest)
    743     (c1 value rest))
    744 
    745 orC = (c1 c2 value rest :
    746   matchResult
    747     (msg _ : c2 value rest)
    748     (v _ : contractOk v rest)
    749     (c1 value rest))
    750 
    751 notC = (c value rest :
    752   matchResult
    753     (msg _ : contractOk value rest)
    754     (_ _ : contractErr "notC: predicate succeeded" rest)
    755     (c value rest))
    756 
    757 mapC = (f c value rest :
    758   matchResult
    759     (msg _ : contractErr msg rest)
    760     (v _ : contractOk (f v) rest)
    761     (c value rest))
    762 
    763 bindC = (c f value rest :
    764   matchResult
    765     (msg _ : contractErr msg rest)
    766     (v _ : f v value rest)
    767     (c value rest))
    768 
    769 -- ---------------------------------------------------------------------------
    770 -- Collection contracts
    771 -- ---------------------------------------------------------------------------
    772 
    773 listOf = (c value rest :
    774   y (self orig xs :
    775     matchList
    776       (contractOk orig rest)
    777       (h r :
    778         matchResult
    779           (msg _ : contractErr msg rest)
    780           (_ _ : self orig r)
    781           (c h rest))
    782       xs) value value)
    783 
    784 nonEmptyListOf = (c :
    785   andC (guardC "empty list" (xs : not? (emptyList? xs))) (listOf c))
    786 
    787 pairOf = (c1 c2 p rest :
    788   matchPair
    789     (a b :
    790       matchResult
    791         (msg _ : contractErr msg rest)
    792         (a' _ :
    793           matchResult
    794             (msg _ : contractErr msg rest)
    795             (b' _ : contractOk (pair a' b') rest)
    796             (c2 b rest))
    797         (c1 a rest))
    798     p)
    799 
    800 -- ---------------------------------------------------------------------------
    801 -- Higher-order function contracts
    802 --
    803 -- These return a Result-wrapped proxy. The proxy itself is a contract: it
    804 -- checks arguments on the way in and results on the way out.
    805 -- ---------------------------------------------------------------------------
    806 
    807 fnContract = (argC resC f rest :
    808   contractOk
    809     (x : (rest1 :
    810       matchResult
    811         (msg _ : contractErr msg rest1)
    812         (x' _ :
    813           matchResult
    814             (msg _ : contractErr msg rest1)
    815             (y _ : contractOk y rest1)
    816             (withContract resC (f x')))
    817         (withContract argC x)))
    818     rest)
    819 
    820 fn2 = (arg1C arg2C resC f rest :
    821   contractOk
    822     (x : (rest1 :
    823       matchResult
    824         (msg _ : contractErr msg rest1)
    825         (x' _ :
    826           contractOk
    827             (y : (rest2 :
    828               matchResult
    829                 (msg _ : contractErr msg rest2)
    830                 (y' _ :
    831                   matchResult
    832                     (msg _ : contractErr msg rest2)
    833                     (z _ : contractOk z rest2)
    834                     (withContract resC (f x' y')))
    835                 (withContract arg2C y)))
    836             rest1)
    837         (withContract arg1C x)))
    838     rest)
    839 
    840 -- ---------------------------------------------------------------------------
    841 -- Interaction-tree effect layer
    842 --
    843 -- These constructors and combinators layer catchable, composable failures on
    844 -- top of the core Result contracts. They reuse the same tags as tricu IO:
    845 --   0 = pureE
    846 --   1 = bindE
    847 --   2 = exceptE
    848 -- ---------------------------------------------------------------------------
    849 
    850 pureE = (value : pair 0 value)
    851 bindE = (action k : pair 1 (pair action k))
    852 exceptE = (tag value k : pair 2 (pair tag (pair value k)))
    853 
    854 pureM = pureE
    855 bindM = bindE
    856 
    857 -- Lift a contract failure into an interaction tree.
    858 checkM = (contract value :
    859   matchResult
    860     (msg _ : exceptE "contract" msg (_ : pureE t))
    861     (checked _ : pureE checked)
    862     (contract value t))
    863 
    864 -- Lift a pure function into the interaction tree.
    865 liftM = (f : (x : pureE (f x)))
    866 
    867 -- Interpret a pure interaction tree into a Result.
    868 runM = (tree :
    869   run tree
    870   where run =
    871           y (self tree :
    872             matchPair
    873               (op payload :
    874                 matchBool
    875                   -- pureE
    876                   (contractOk (snd tree) t)
    877                   (matchBool
    878                     -- bindE
    879                     (matchPair
    880                       (action k :
    881                         matchResult
    882                           (msg _ : contractErr msg t)
    883                           (v _ : self (k v))
    884                           (self action))
    885                       payload)
    886                     -- exceptE
    887                     (matchPair
    888                       (tag pair :
    889                         matchPair
    890                           (value k :
    891                             contractErr value t)
    892                           pair)
    893                       payload)
    894                     (equal? op 1))
    895                   (equal? op 0))
    896               tree))
    897 
    898 -- Handle matching exceptE nodes by applying the handler to the value and the
    899 -- resumption continuation. Non-matching exceptions are left in place.
    900 handleM = (tag handler tree :
    901   handle tree
    902   where handle =
    903           y (self tree :
    904             matchPair
    905               (op payload :
    906                 matchBool
    907                   -- pureE
    908                   tree
    909                   (matchBool
    910                     -- bindE
    911                     (matchPair
    912                       (action k :
    913                         bindE (self action) (v : self (k v)))
    914                       payload)
    915                     -- exceptE
    916                     (matchPair
    917                       (et pair :
    918                         matchPair
    919                           (value k :
    920                             matchBool
    921                               (self (handler value k))
    922                               tree
    923                               (equal? et tag))
    924                           pair)
    925                       payload)
    926                     (equal? op 1))
    927                   (equal? op 0))
    928               tree))
    929