tricu

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

ece0fc4491038b19533f3eb18e9a039c4f23798e307a2c9edc90919a389697a3.tri (21471B)


      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 = (onTrue onFalse : triage
     26   onFalse
     27   (_ : onTrue)
     28   (_ _ : onTrue)
     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 -- Keep these eliminators: patterned traversal has higher measured runtime and
    335 -- allocation costs here. Local workers capture callbacks rather than threading
    336 -- them through every recursive call.
    337 map f xs = (let rec
    338   walk remaining = (matchList
    339     t
    340     (h r : pair (f h) (walk r))
    341     remaining)
    342   in walk xs)
    343 
    344 filter f xs = (let rec
    345   walk remaining = (matchList
    346     t
    347     (h r : matchBool (pair h (walk r)) (walk r) (f h))
    348     remaining)
    349   in walk xs)
    350 
    351 foldl f initial xs = (let rec
    352   walk remaining acc = (matchList
    353     acc
    354     (h r : walk r (f acc h))
    355     remaining)
    356   in walk xs initial)
    357 
    358 foldr_ self l f x =
    359   matchList
    360     x
    361     (h r : f (self r f x) h)
    362     l
    363 foldr = f x l : y foldr_ l f x
    364 
    365 length_ self xs =
    366   matchList
    367     0
    368     (_ r : succ (self r))
    369     xs
    370 length @(listOf anyC) =@nat? y length_
    371 
    372 reverse xs = (let rec
    373   walk remaining acc = (matchList
    374     acc
    375     (h r : walk r (pair h acc))
    376     remaining)
    377   in walk xs t)
    378 
    379 snoc_ self x xs =
    380   matchList
    381     (pair x t)
    382     (h r : pair h (self x r))
    383     xs
    384 snoc = x xs : y snoc_ x xs
    385 
    386 count_ self x xs =
    387   matchList
    388     0
    389     (h r :
    390       matchBool
    391         (succ (self x r))
    392         (self x r)
    393         (equal? x h))
    394     xs
    395 count = x xs : y count_ x xs
    396 
    397 last_ self xs =
    398   matchList
    399     t
    400     (h r :
    401       matchBool
    402         h
    403         (self r)
    404         (emptyList? r))
    405     xs
    406 last @(nonEmptyListOf anyC) =@anyC y last_
    407 
    408 all?_ self pred xs =
    409   matchList
    410     true
    411     (h r : and? (pred h) (self pred r))
    412     xs
    413 all? = pred xs : y all?_ pred xs
    414 
    415 any?_ self pred xs =
    416   matchList
    417     false
    418     (h r : or? (pred h) (self pred r))
    419     xs
    420 any? = pred xs : y any?_ pred xs
    421 
    422 intersect = xs ys : filter (x : lExist? x ys) xs
    423 
    424 nth_ self xs n i =
    425   matchList
    426     t
    427     (h r :
    428       matchBool
    429         h
    430         (self r n (succ i))
    431         (equal? i n))
    432     xs
    433 nth = n xs : y nth_ xs n 0
    434 
    435 headMaybe = matchList nothing (h _ : just h)
    436 
    437 lastMaybe_ self xs =
    438   matchList
    439     nothing
    440     (h r :
    441       matchBool
    442         (just h)
    443         (self r)
    444         (emptyList? r))
    445     xs
    446 lastMaybe = xs : y lastMaybe_ xs
    447 
    448 nthMaybe_ self xs n i =
    449   matchList
    450     nothing
    451     (h r :
    452       matchBool
    453         (just h)
    454         (self r n (succ i))
    455         (equal? i n))
    456     xs
    457 nthMaybe = n xs : y nthMaybe_ xs n 0
    458 
    459 take_ self xs n i =
    460   matchList
    461     t
    462     (h r :
    463       matchBool
    464         t
    465         (pair h (self r n (succ i)))
    466         (equal? i n))
    467     xs
    468 take = n xs : y take_ xs n 0
    469 
    470 drop_ self xs n i =
    471   matchBool
    472     xs
    473     (matchList
    474       t
    475       (_ r : self r n (succ i))
    476       xs)
    477     (equal? i n)
    478 drop = n xs : y drop_ xs n 0
    479 
    480 splitAt = n xs : pair (take n xs) (drop n xs)
    481 
    482 concatMap_ self f xs =
    483   matchList
    484     t
    485     (h r : append (f h) (self f r))
    486     xs
    487 concatMap = f xs : y concatMap_ f xs
    488 
    489 find_ self pred xs =
    490   matchList
    491     nothing
    492     (h r :
    493       matchBool
    494         (just h)
    495         (self pred r)
    496         (pred h))
    497     xs
    498 find = pred xs : y find_ pred xs
    499 
    500 partition_ self pred xs trues falses =
    501   matchList
    502     (pair (reverse trues) (reverse falses))
    503     (h r :
    504       matchBool
    505         (self pred r (pair h trues) falses)
    506         (self pred r trues (pair h falses))
    507         (pred h))
    508     xs
    509 partition = pred xs : y partition_ pred xs t t
    510 
    511 strLength = length
    512 strAppend = append
    513 strEq? = equal?
    514 strEmpty? = emptyList?
    515 
    516 startsWith?_ self prefix input =
    517   matchList
    518     true
    519     (ph pr :
    520       matchList
    521         false
    522         (sh sr :
    523           matchBool
    524             (self pr sr)
    525             false
    526             (equal? ph sh))
    527         input)
    528     prefix
    529 startsWith? = prefix input : y startsWith?_ prefix input
    530 
    531 endsWith? = prefix str : startsWith? (reverse prefix) (reverse str)
    532 
    533 contains?_ self needle haystack =
    534   matchBool
    535     true
    536     (matchList
    537       false
    538       (_ r : self needle r)
    539       haystack)
    540     (startsWith? needle haystack)
    541 contains? = needle haystack : y contains?_ needle haystack
    542 
    543 sum @(listOf nat?) =@nat? foldl (acc x : add x acc) 0
    544 product @(listOf nat?) =@nat? foldl (acc x : mul x acc) 1
    545 
    546 -- ---------------------------------------------------------------------------
    547 -- Generic separators
    548 --
    549 -- `lines`, `unlines`, `words` and `unwords` at the bottom of this section are
    550 -- the byte-valued special cases of these primitives.
    551 --
    552 -- Joining takes any separator; splitting takes one byte. Separators are removed
    553 -- rather than kept, and empty fields are preserved.
    554 -- ---------------------------------------------------------------------------
    555 
    556 takeWhile f xs = (let rec
    557   walk remaining = (lazyList
    558     (_ : t)
    559     (h r : case f h of
    560       t = t
    561       _ = pair h (walk r))
    562     remaining)
    563   in walk xs)
    564 
    565 dropWhile f xs = (let rec
    566   walk remaining = (lazyList
    567     (_ : t)
    568     (h r : case f h of
    569       t = pair h r
    570       _ = walk r)
    571     remaining)
    572   in walk xs)
    573 
    574 -- Byte-level whitespace only: space and horizontal tab (HTTP OWS).
    575 spaceByte? = b : equal? b 32
    576 tabByte?   = b : equal? b 9
    577 trimByte?  = b : or? (spaceByte? b) (tabByte? b)
    578 
    579 trim = xs : dropWhile trimByte? (reverse (dropWhile trimByte? (reverse xs)))
    580 
    581 intercalate sep xs = (let rec
    582   walk remaining = (lazyList
    583     (_ : t)
    584     (h r : case emptyList? r of
    585       t = append h (append sep (walk r))
    586       _ = h)
    587     remaining)
    588   in walk xs)
    589 
    590 -- Separator after every field, including the last one. Line-oriented formats
    591 -- want this: `joinSuffix "\n" xs` terminates the final line while
    592 -- `intercalate "\n" xs` does not.
    593 joinSuffix sep xs = (let rec
    594   walk remaining = (lazyList
    595     (_ : t)
    596     (h r : append (append h sep) (walk r))
    597     remaining)
    598   in walk xs)
    599 
    600 -- Split on a single byte.
    601 -- Empty fields are preserved: `splitOnByte 58 "a::b"` is ["a" "" "b"].
    602 -- Keep the two small thunks here: case dispatch and extra local branch
    603 -- bindings both add measurable per-byte costs in the splitter.
    604 splitOnByte byte str = (let rec
    605   walk remaining reversedFields reversedField = (lazyList
    606     (_ : map reverse (reverse (pair reversedField reversedFields)))
    607     (h r : lazyBool
    608       (_ : walk r (pair reversedField reversedFields) t)
    609       (_ : walk r reversedFields (pair h reversedField))
    610       (equal? h byte))
    611     remaining)
    612   in walk str t t)
    613 
    614 -- Every one of these keeps its arguments bound: partially applying a
    615 -- multi-argument function at the top level leaves a fixed point exposed.
    616 lines   = str : splitOnByte 10 str
    617 unlines = xs : joinSuffix "\n" xs
    618 
    619 -- Runs of separators collapse: empty fields are dropped.
    620 words   = str : filter (w : not? (emptyList? w)) (splitOnByte 32 str)
    621 unwords = xs : intercalate " " xs
    622 
    623 zipWith_ self f xs ys =
    624   matchList
    625     t
    626     (xh xt :
    627       matchList
    628         t
    629         (yh yt : pair (f xh yh) (self f xt yt))
    630         ys)
    631     xs
    632 zipWith = f xs ys : y zipWith_ f xs ys
    633 
    634 -- ---------------------------------------------------------------------------
    635 -- Core contract type
    636 --
    637 -- A contract is an ordinary tricu function: Tree -> Tree -> Result Tree Tree.
    638 -- The second argument is the conventional "rest" slot. On success a contract
    639 -- returns the checked value wrapped in the standard ok shape; on failure it
    640 -- returns a diagnostic wrapped in the standard err shape.
    641 --
    642 -- The contract kernel is a globally configurable function selected by the
    643 -- runner. It decides whether to accept the contract result, replace it, log
    644 -- it, or transform the diagnostic. The default kernel is the identity on the
    645 -- contract Result.
    646 --
    647 -- The runner may rebind 'kernel' to a different kernel before evaluating
    648 -- user code (e.g. via --contract-kernel).
    649 -- ---------------------------------------------------------------------------
    650 
    651 contractOk = (value : (rest : ok value rest))
    652 contractErr = (msg : (rest : err msg rest))
    653 
    654 -- Default contract kernel. Return the contract Result unchanged.
    655 defaultKernel = (contract value result :
    656   matchResult
    657     (msg rest : err msg rest)
    658     (v rest : ok v rest)
    659     result)
    660 
    661 -- The active kernel. The runner may rebind this name to a different kernel
    662 -- before evaluating user code (e.g. via --contract-kernel). Internally,
    663 -- withContract dispatches through this binding, so rebinding 'kernel' changes
    664 -- the behaviour of every contract boundary in the program.
    665 kernel = defaultKernel
    666 
    667 -- Skip-everything kernel. Resume with the original value on failure.
    668 skipKernel = (contract value result :
    669   matchResult
    670     (msg rest : ok value rest)
    671     (v rest : ok v rest)
    672     result)
    673 
    674 -- Apply a contract and pass the raw Result to the kernel.
    675 withContract = (contract value :
    676   kernel contract value (contract value t))
    677 
    678 -- Apply a contract and return the checked value or the diagnostic message.
    679 check contract value =
    680   matchResult
    681     (msg _ : msg)
    682     (v _ : v)
    683     (withContract contract value)
    684 
    685 -- Apply a contract and return the raw Result (kernel is bypassed).
    686 checkContract = (contract value : contract value t)
    687 
    688 -- ---------------------------------------------------------------------------
    689 -- Basic contracts
    690 -- ---------------------------------------------------------------------------
    691 
    692 -- Any value passes.
    693 anyC = (value : contractOk value)
    694 
    695 -- Always fails with the supplied message.
    696 neverC = (msg : (value : contractErr msg))
    697 
    698 -- Build a contract from a predicate that inspects only the value.
    699 guardC msg predicate value rest = case predicate value of
    700   t = contractErr msg rest
    701   _ = contractOk value rest
    702 
    703 -- Structural natural-number predicate.
    704 -- A natural is either Leaf (0) or Fork bit rest where bit is Leaf (even)
    705 -- or Stem Leaf (odd) and rest is itself a natural.
    706 isNat? = y (self n :
    707   triage
    708     true
    709     (_ : false)
    710     (bit r :
    711       triage
    712         (self r)
    713         (_ : self r)
    714         (_ _ : false)
    715         bit)
    716     n)
    717 
    718 -- Natural number contract.
    719 nat? = guardC "not a natural number" isNat?
    720 
    721 -- Non-zero natural number contract.
    722 nonZero? = guardC "non-zero" (n : and? (isNat? n) (not? (isZero_? n)))
    723 
    724 -- Boolean contract.
    725 bool? = guardC "not a boolean" (b : or? (equal? b true) (equal? b false))
    726 
    727 -- ---------------------------------------------------------------------------
    728 -- Contract combinators
    729 -- ---------------------------------------------------------------------------
    730 
    731 andC = (c1 c2 value rest :
    732   matchResult
    733     (msg _ : contractErr msg rest)
    734     (v _ : c2 v rest)
    735     (c1 value rest))
    736 
    737 orC = (c1 c2 value rest :
    738   matchResult
    739     (msg _ : c2 value rest)
    740     (v _ : contractOk v rest)
    741     (c1 value rest))
    742 
    743 notC = (c value rest :
    744   matchResult
    745     (msg _ : contractOk value rest)
    746     (_ _ : contractErr "notC: predicate succeeded" rest)
    747     (c value rest))
    748 
    749 mapC = (f c value rest :
    750   matchResult
    751     (msg _ : contractErr msg rest)
    752     (v _ : contractOk (f v) rest)
    753     (c value rest))
    754 
    755 bindC = (c f value rest :
    756   matchResult
    757     (msg _ : contractErr msg rest)
    758     (v _ : f v value rest)
    759     (c value rest))
    760 
    761 -- ---------------------------------------------------------------------------
    762 -- Collection contracts
    763 -- ---------------------------------------------------------------------------
    764 
    765 listOf = (c value rest :
    766   y (self orig xs :
    767     matchList
    768       (contractOk orig rest)
    769       (h r :
    770         matchResult
    771           (msg _ : contractErr msg rest)
    772           (_ _ : self orig r)
    773           (c h rest))
    774       xs) value value)
    775 
    776 nonEmptyListOf = (c :
    777   andC (guardC "empty list" (xs : not? (emptyList? xs))) (listOf c))
    778 
    779 pairOf = (c1 c2 p rest :
    780   matchPair
    781     (a b :
    782       matchResult
    783         (msg _ : contractErr msg rest)
    784         (a' _ :
    785           matchResult
    786             (msg _ : contractErr msg rest)
    787             (b' _ : contractOk (pair a' b') rest)
    788             (c2 b rest))
    789         (c1 a rest))
    790     p)
    791 
    792 -- ---------------------------------------------------------------------------
    793 -- Higher-order function contracts
    794 --
    795 -- These return a Result-wrapped proxy. The proxy itself is a contract: it
    796 -- checks arguments on the way in and results on the way out.
    797 -- ---------------------------------------------------------------------------
    798 
    799 fnContract = (argC resC f rest :
    800   contractOk
    801     (x : (rest1 :
    802       matchResult
    803         (msg _ : contractErr msg rest1)
    804         (x' _ :
    805           matchResult
    806             (msg _ : contractErr msg rest1)
    807             (y _ : contractOk y rest1)
    808             (withContract resC (f x')))
    809         (withContract argC x)))
    810     rest)
    811 
    812 fn2 = (arg1C arg2C resC f rest :
    813   contractOk
    814     (x : (rest1 :
    815       matchResult
    816         (msg _ : contractErr msg rest1)
    817         (x' _ :
    818           contractOk
    819             (y : (rest2 :
    820               matchResult
    821                 (msg _ : contractErr msg rest2)
    822                 (y' _ :
    823                   matchResult
    824                     (msg _ : contractErr msg rest2)
    825                     (z _ : contractOk z rest2)
    826                     (withContract resC (f x' y')))
    827                 (withContract arg2C y)))
    828             rest1)
    829         (withContract arg1C x)))
    830     rest)
    831 
    832 -- ---------------------------------------------------------------------------
    833 -- Interaction-tree effect layer
    834 --
    835 -- These constructors and combinators layer catchable, composable failures on
    836 -- top of the core Result contracts. They reuse the same tags as tricu IO:
    837 --   0 = pureE
    838 --   1 = bindE
    839 --   2 = exceptE
    840 -- ---------------------------------------------------------------------------
    841 
    842 pureE = (value : pair 0 value)
    843 bindE = (action k : pair 1 (pair action k))
    844 exceptE = (tag value k : pair 2 (pair tag (pair value k)))
    845 
    846 pureM = pureE
    847 bindM = bindE
    848 
    849 -- Lift a contract failure into an interaction tree.
    850 checkM = (contract value :
    851   matchResult
    852     (msg _ : exceptE "contract" msg (_ : pureE t))
    853     (checked _ : pureE checked)
    854     (contract value t))
    855 
    856 -- Lift a pure function into the interaction tree.
    857 liftM = (f : (x : pureE (f x)))
    858 
    859 -- Interpret a pure interaction tree into a Result.
    860 runM tree = (matchPair interpret tree
    861   where
    862     interpret op payload = case op of
    863       0 = contractOk payload t
    864       1 = (matchPair
    865         (action k : matchResult
    866           (msg _ : contractErr msg t)
    867           (value _ : runM (k value))
    868           (runM action))
    869         payload)
    870       _ = (matchPair
    871         (_ detail : matchPair (value _ : contractErr value t) detail)
    872         payload))
    873 
    874 -- Handle matching exceptE nodes by applying the handler to the value and the
    875 -- resumption continuation. Non-matching exceptions are left in place.
    876 handleM tag handler tree = (let rec
    877   handle current = (matchPair interpret current
    878     where
    879       choose exceptionTag value k = case equal? exceptionTag tag of
    880         t = current
    881         _ = handle (handler value k)
    882       handleException exceptionTag detail = matchPair (choose exceptionTag) detail
    883       interpret op payload = case op of
    884         0 = current
    885         1 = (matchPair
    886           (action k : bindE (handle action) (value : handle (k value)))
    887           payload)
    888         _ = matchPair handleException payload)
    889   in handle tree)