urbit/lib/async.hoon

191 lines
5.3 KiB
Plaintext
Raw Normal View History

|* [input-type=mold card-type=mold contract-type=mold]
2019-05-23 22:26:53 +03:00
|%
2019-06-01 00:44:47 +03:00
+$ async-input [=bowl:gall in=(unit [=wire sign=input-type])]
+$ async-move (pair bone card-type)
2019-05-23 22:26:53 +03:00
::
2019-05-31 00:43:27 +03:00
:: cards: cards to send immediately. These will go out even if a
:: later stage of the computation fails, so they shouldn't have
:: any semantic effect on the rest of the system.
:: Alternately, they may record an entry in contracts with
:: enough information to undo the effect if the computation
:: fails.
:: effects: moves to send after the computation ends.
:: contracts: stuff to cancel at end of computation.
2019-05-29 01:38:14 +03:00
:: wait: don't move on, stay here. The next sign should come back
:: to this same callback.
2019-05-31 00:43:27 +03:00
:: cont: continue computation with new callback.
:: fail: abort computation; don't send effects
:: done: finish computation; send effects
2019-05-23 22:26:53 +03:00
::
2019-06-01 00:44:47 +03:00
++ async-output-raw
2019-05-23 22:26:53 +03:00
|* a=mold
$~ [~ ~ ~ %done *a]
2019-05-31 00:43:27 +03:00
$: cards=(list card-type)
2019-06-01 00:44:47 +03:00
effects=(list async-move)
contracts=(set [add=? contract=contract-type])
2019-05-23 22:26:53 +03:00
$= next
$% [%wait ~]
2019-06-01 00:44:47 +03:00
[%cont self=(async-form-raw a)]
2019-05-23 22:26:53 +03:00
[%fail err=(pair term tang)]
[%done value=a]
==
==
::
2019-06-01 00:44:47 +03:00
++ async-form-raw
2019-05-23 22:26:53 +03:00
|* a=mold
2019-06-01 00:44:47 +03:00
$-(async-input (async-output-raw a))
2019-05-23 22:26:53 +03:00
::
2019-05-31 00:43:27 +03:00
:: Abort asynchronous computation with error message
::
2019-06-01 00:44:47 +03:00
++ async-fail
2019-05-23 22:26:53 +03:00
|= err=(pair term tang)
2019-06-01 00:44:47 +03:00
|= async-input
[~ ~ ~ %fail err]
2019-05-23 22:26:53 +03:00
::
2019-05-31 00:43:27 +03:00
:: Asynchronous transcaction monad.
::
:: Combo of four monads:
:: - Reader on input-type
:: - Writer on card-type
:: - Continuation
:: - Exception
::
2019-06-01 00:44:47 +03:00
++ async
2019-05-23 22:26:53 +03:00
|* a=mold
|%
2019-06-01 00:44:47 +03:00
++ output (async-output-raw a)
2019-05-31 00:43:27 +03:00
::
:: Type of an asynchronous computation.
::
2019-06-01 00:44:47 +03:00
++ form (async-form-raw a)
2019-05-31 00:43:27 +03:00
::
:: Monadic pure. Identity computation for bind.
::
2019-05-23 22:26:53 +03:00
++ pure
|= arg=a
^- form
2019-06-01 00:44:47 +03:00
|= async-input
[~ ~ ~ %done arg]
2019-05-23 22:26:53 +03:00
::
2019-05-31 00:43:27 +03:00
:: Monadic bind. Combines two computations, associatively.
::
2019-05-23 22:26:53 +03:00
++ bind
|* b=mold
2019-06-01 00:44:47 +03:00
|= [m-b=(async-form-raw b) fun=$-(b form)]
2019-05-23 22:26:53 +03:00
^- form
2019-06-01 00:44:47 +03:00
|= input=async-input
=/ b-res=(async-output-raw b)
2019-05-23 22:26:53 +03:00
(m-b input)
^- output
2019-05-31 00:43:27 +03:00
:^ cards.b-res effects.b-res contracts.b-res
2019-05-23 22:26:53 +03:00
?- -.next.b-res
%wait [%wait ~]
%cont [%cont ..$(m-b self.next.b-res)]
%fail [%fail err.next.b-res]
%done [%cont (fun value.next.b-res)]
==
::
2019-06-01 00:44:47 +03:00
:: The async monad must be evaluted in a particular way to maintain
2019-05-23 22:26:53 +03:00
:: its monadic character. +take:eval implements this.
::
++ eval
|%
2019-06-01 00:44:47 +03:00
:: Indelible state of a async
2019-05-23 22:26:53 +03:00
::
+$ eval-form
2019-06-01 00:44:47 +03:00
$: effects=(list async-move)
contracts=(set contract-type)
2019-05-23 22:26:53 +03:00
=form
==
::
:: Convert initial form to eval-form
::
++ from-form
|= =form
^- eval-form
[~ ~ form]
2019-05-23 22:26:53 +03:00
::
:: The cases of results of +take
::
+$ eval-result
$% [%next ~]
[%fail contracts=(set contract-type) err=(pair term tang)]
[%done contracts=(set contract-type) value=a]
2019-05-23 22:26:53 +03:00
==
::
2019-06-01 00:44:47 +03:00
:: Take a new sign and run the async against it
2019-05-23 22:26:53 +03:00
::
++ take
:: moves: accumulate throughout recursion the moves to be
:: produced now
2019-06-01 00:44:47 +03:00
=| moves=(list async-move)
|= [=eval-form =bone =async-input]
^- [[(list async-move) =eval-result] _eval-form]
=* take-loop $
2019-06-01 00:44:47 +03:00
:: run the async callback
2019-05-23 22:26:53 +03:00
::
2019-06-01 00:44:47 +03:00
=/ =output (form.eval-form async-input)
2019-05-31 00:43:27 +03:00
:: add cards to moves
2019-05-23 22:26:53 +03:00
::
=. moves
%+ welp
moves
2019-05-31 00:43:27 +03:00
%+ turn cards.output
|= card=card-type
2019-06-01 00:44:47 +03:00
^- async-move
2019-05-23 22:26:53 +03:00
[bone card]
:: add effects to list to be produced when done
::
=. effects.eval-form
(weld effects.eval-form effects.output)
:: add or remove contracts
::
=. .
=* loop-result .
=/ new=(list [add=? contract=contract-type])
~(tap in contracts.output)
|- ^+ loop-result
=* loop $
?~ new
loop-result
?: add.i.new
?: (~(has in contracts.eval-form) contract.i.new)
%= loop-result
next.output [%fail %contract-already-exists >contract.i.new< ~]
==
%= loop
contracts.eval-form (~(put in contracts.eval-form) contract.i.new)
new t.new
==
?: (~(has in contracts.eval-form) contract.i.new)
%= loop
contracts.eval-form (~(del in contracts.eval-form) contract.i.new)
new t.new
==
%= loop-result
next.output [%fail %contract-doesnt-exist >contract.i.new< ~]
==
2019-05-23 22:26:53 +03:00
:: if done, produce effects
::
=? moves ?=(%done -.next.output)
%+ welp
moves
2019-05-29 01:38:14 +03:00
effects.eval-form
2019-05-23 22:26:53 +03:00
:: case-wise handle next steps
::
?- -.next.output
%wait [[moves %next ~] eval-form]
%fail [[moves %fail contracts.eval-form err.next.output] eval-form]
%done [[moves %done contracts.eval-form value.next.output] eval-form]
2019-05-23 22:26:53 +03:00
%cont
:: recurse to run continuation with initialization input
::
%_ take-loop
2019-05-23 22:26:53 +03:00
form.eval-form self.next.output
2019-06-01 00:44:47 +03:00
async-input [bowl.async-input ~]
2019-05-23 22:26:53 +03:00
==
==
--
--
--