16 | -- An effectful computation that will be run when evaluating
17 | -- a Pull
27 | --------------------------------------------------------------------------------
28 | -- Pull Type
29 | --------------------------------------------------------------------------------
31 | ||| A `Pull f o es r` is a - possibly infinite - series of effectful
32 | ||| computations running in monad `f`, producing an arbitrary sequence
33 | ||| of chunks of values of type `c o` along the way, that eventually
34 | ||| terminates either with an error of type `HSum es` or with a result
35 | ||| of type `r`.
36 | |||
37 | ||| A `Pull` is a monad with relation to `r`, so a sequencing of `Pull`s
38 | ||| via bind `(>>=)` means a sequencing of the chunks of output generated
39 | ||| along the way. For instance, the following `Pull` will produce the
40 | ||| values `[1,2,3,4,5,6]` when being run:
41 | |||
42 | ||| ```idris2
43 | ||| emit [1,2,3] >> emit [4,5,6]
44 | ||| ```
45 | |||
46 | ||| A `Pull` provides capabilities for safe resource management via
47 | ||| `FS.Scope`s, allowing us to release allocated resources as soon as
48 | |||
49 | ||| * the pull is exhausted and produces no more output.
50 | ||| * evaluating the pull aborts with an error.
51 | ||| * a pull will not longer be asked to produce additional values
52 | ||| because an outer pull has been exhaused.
53 | |||
54 | ||| The last case occurs for instance when we stop evaluating a `Stream`
55 | ||| via `take` or `takeWhile`.
56 | |||
57 | ||| About the effect type `f`: Most non-trivial `Pull`s and `Stream`s come
58 | ||| with the potential of failure. In addition, running a `Pull` to completion
59 | ||| requires (thread-local) mutable state to keep tracks of the evaluation
60 | ||| scopes. Effect type `f` must therefore implement interface `Data.Linear.ELift1`
61 | ||| at the very least. This still allows us to run a `Pull` or `Stream`
62 | ||| as a pure computation, for instance when using `Control.Monad.Elin`.
63 | |||
64 | ||| Most of the time, however, we are interested in streams that produce
65 | ||| arbitrary side-effects, and an I/O monad such as `IO.Async` is required.
66 | |||
67 | ||| A note on totality: Theoretically, we should be able define `Bind` as follows:
68 | |||
69 | ||| ```idris
70 | ||| Bind : Pull f o es r -> Inf (r -> Pull f o es s) -> Pull f o es s
71 | ||| ```
72 | |||
73 | ||| This would allow us to safely generate total, infinite streams of output
74 | ||| and only running a `Pull` would be a non-total function. In practice, however,
75 | ||| I found this approach to have various shortcomings: We'd need a custom
76 | ||| bind operator for defining infinite pulls. That's not hard to implement, but
77 | ||| I found it to be fairly limited and cumbersome to use, especially since
78 | ||| type inference got very erratic. For the time being, I therefore decided
79 | ||| to resort to `assert_total` when defining potentially infinite streams.
80 | ||| It's not pretty, but it gets the job done.
81 | export
89 | ||| Constructor for producing value of type `r`
92 | ||| Constructor for failing with an exception
95 | ||| Constructor for producing a chunk of output values
98 | ||| Wraps an arbitrary effectful computation in a `Pull`
101 | ||| Unwraps the given child `Pull` until it either produces
102 | ||| a result or a chunk of output.
105 | ||| Attempts to evaluate the underlying `Pull`, returning any error
106 | ||| wrapped in a `Left` and a successful result in a `Right`. This is
107 | ||| the primitive used for error handling.
110 | ||| Sequencing of `Pull`s via monadic bind `(>>=)`.
113 | ||| Runs the given `Pull` in a new child scope. The optional second argument
114 | ||| is used to check, if the scope has been interrupted.
117 | ||| Internal: A pull for returning the current scope
120 | ||| Internal: Forces the given pull to be evaluated in the given scope.
121 | |||
122 | ||| This is used when evaluating pulls in parallel (for instance, when zipping
123 | ||| or merging streams): Both pulls must be run in the outer scope to prevent
124 | ||| the resources of the second pull to be release early when the first once
125 | ||| is exhausted. See `stepLeg` and `StepLeg`.
128 | ||| Continues with the second pull in case the first is interrupted.
131 | ||| Alias for a `Pull` with a trivial return type.
136 | ||| Alias for a `Pull` that cannot produce any output.
141 | ||| Alias for a `Pull` that cannot produce any output.
146 | ||| A (partially evaluated) `Pull` plus the scope it should be
147 | ||| evaluated in.
155 | --------------------------------------------------------------------------------
156 | -- Primitives
157 | --------------------------------------------------------------------------------
159 | ||| Lifts the given effectful computation into a `Pull`.
164 | ||| Acquires a resource that will be released once the current
165 | ||| scope is cleaned up.
170 | ||| Prepends the given output to a pull.
175 | ||| Emits a single chunk of output.
180 | ||| Unwraps a `pull` either returning its final result or
181 | ||| its first output together with the remainder of the pull.
186 | ||| Returns the current evaluation scope.
187 | |||
188 | ||| This is an internal primitive that can be used to implement
189 | ||| new combinators and topologies.
194 | ||| Creates a new scope for running the given `Pull` in
199 | ||| Forces the given `Pull` to be evaluated in the given scope.
200 | |||
201 | ||| This is an internal primitive that can be used to implement
202 | ||| new combinators and topologies.
207 | ||| Error handling primitive: Wraps a successful result in a `Right`
208 | ||| and an error in a `Left`.
220 | ||| Runs the given pull in a new child scope and interrupts
221 | ||| its evaluation once the given `Deferred` is completed.
222 | export
229 | ||| Runs the given pull in a new child scope and interrupts
230 | ||| its evaluation once the given `Deferred` is completed.
231 | export
238 | --------------------------------------------------------------------------------
239 | -- Interfaces
240 | --------------------------------------------------------------------------------
242 | ||| A `Pull` is a `Functor`, `Applicative`, and `Monad`, all of which
243 | ||| follow from it implementing `MErr`, which adds capabilities for error
244 | ||| handling.
254 | ||| In case the effect type of a pull has support for mutable state in
255 | ||| state thread `s`, so does the `Pull` itself.
272 | --------------------------------------------------------------------------------
273 | -- Evaluating Pulls
274 | --------------------------------------------------------------------------------
276 | -- The general strategy is to convert a Pull into a running computation
277 | -- with a stack of items itentifying the tasks that have yet to be
278 | -- finished. See also `Stack`
285 | -- the continuation of a monadic bind on the stack
288 | -- `Att` (error handling) on the stack
291 | -- Handling of output (`Uncons`)
294 | -- Changing the current evaluation scope (resource management)
295 | -- `cl` signifies whether the current scope should be closed and
296 | -- its resources cleaned up.
299 | -- Resumption to use if the current pull has been interrupted.
302 | -- Call stack used to store computational steps that are yet to follow.
312 | -- Unfolding of a `Pull`, which either arrives at an effectful
313 | -- computation (an `Action`) plus a call stack, or a final result.
322 | -- Result of `traceOutput`: Output (generated via the `Cons` data constructor)
323 | -- can *only* be handled via the `Uncons` data constructor. Therefore, whenever
324 | -- a `Cons` is encountered, the current stack is traversed until we arrive
325 | -- at a `U` item (representing an `Uncons`), and the previously unfolded Pull
326 | -- is reassembled during this search. The output plus reassebled pull are then
327 | -- paired and wrapped in a `Right`.
336 | -- Traverses the stack until an `Unfold` (`U` item) is encountered.
337 | -- The stack's prefix is reassembled into a Pull that is then
338 | -- used as the continuation together with the current output.
358 | -- Tail-recursively unfolds a `Pull` placing all continuations on
359 | -- the call stack until we either arrive at a final result or at
360 | -- an effectful computation (`Action`).
361 | covering
370 | -- This is here for reasons of efficiency: If a `Bind` wraps
371 | -- a pure value, the value can be passed to the continuation
372 | -- directly. Otherwise, the continuation is put on the stack.
376 | -- We arrived at a terminal (a result), and it's time to
377 | -- pass it to the head of the stack (if any).
380 | -- monadic bind: we pass result `v` to continuation `fun`
381 | -- and continue from there
384 | -- `Uncons`: We arrived at the pull's end, so we pass
385 | -- the result wrapped in a `Left` (see the type of `Uncons`)
388 | -- error handling: there was no error so we wrap the result
389 | -- in a `Right`.
392 | -- Scope handling. In case the current scope needs to be
393 | -- closed, we arrived at an effectful computation and return
394 | -- it as our result. Otherwise, we just continue with the
395 | -- new scope.
400 | -- Interrupt handling. We were not interrupted, so we just
401 | -- continue.
405 | -- An error occured. Let's try and handle it.
408 | -- We can't continue with the bind, so we drop it and look
409 | -- if there is an error handler further down the stack.
412 | -- We can't continue with the `Uncons`, so we drop it and look
413 | -- if there is an error handler further down the stack.
416 | -- We found an error handler, so we wrap the error in a `Left`
417 | -- and continue regularily.
420 | -- Scope handling. In case the current scope needs to be
421 | -- closed, we arrived at an effectful computation and return
422 | -- it as our result. Otherwise, we just continue with the
423 | -- new scope.
428 | -- Interrupt handling. We were not interrupted, so we just
429 | -- continue.
433 | -- Some output was produced. The only way this can be handled
434 | -- is by an `Uncons` wrapper, which by now should be on the
435 | -- stack. We try to find and continue from there. This will
436 | -- reassemble parts of the wrapping pull, so we might go
437 | -- back and forth on the stack for some time (until all
438 | -- output has been consumed, or consumption is aborted)
443 | -- An effectful computation. We have to stop here and pass control
444 | -- to `loop`
447 | -- We are looking for some output, so we put an uncons handler
448 | -- `U` on the stack.
451 | -- Error handling. We put the corresponding item onto the stack
452 | -- and continue.
455 | -- Pull `q` should be run in a new scope. We open a new scope
456 | -- and put a note on the stack that the scope should be closed
457 | -- when `q` is done.
462 | -- Pull `q` should be run in the given scope. We put a note
463 | -- on the stack that the current scope `sc` should be used
464 | -- and `s2` closed (if `b` is `True`) once `q` is done.
467 | -- We have been asked for the current scope so we wrap it
468 | -- in a `Val` and continue.
471 | -- An interrupt handler should be put on the stack: `q1`
472 | -- will be evaluated and `q2` should be used in its stead in
473 | -- case it has been interrupted.
476 | -- This is invoked if evaluation of the pull has been interrupted
477 | -- by an external event. We look on the stack for an interrupt handler
478 | -- (item `I`) or - if none is found - terminate with `Canceled`.
479 | covering
482 | -- main run loop: Keeps invoking `view`, executing the actions
483 | -- it gets back until a result is found or evaluation of the
484 | -- pull interrupted by an external event.
485 | covering
488 | -- We check if the current scope has been interrupted
489 | -- If that'e the case, we search for an interrupt handler by
490 | -- passing control to `interrupted`
493 | -- We unfold the current pull until we either arrive at a final
494 | -- result or get an effectful computation we must run in order
495 | -- to continue
499 | -- Effect `act` should be run in monad `f`. This might
500 | -- be something long-running (waiting on a timer, listening
501 | -- on a socket) so it should be possible to interrupt this.
502 | -- TODO: Some (short-running) effects do not require the
503 | -- overhead from making them interruptible. We should
504 | -- consider adding a boolean flag to the `Eval` constructor.
505 | -- The reason this is not already there: It will complicate
506 | -- the API that's based on evaluating effects.
512 | -- We should acquire a resource that should be later released
513 | -- See the notes above about interrupting effects.
514 | -- TODO: This currently does not take cancellation (of `Async`)
515 | -- into account and should be fixed.
521 | -- Scope `old` has come to an end and should be closed, thus
522 | -- releasing all resources that have been aqcuired within.
529 | -- We are done! Yay!
532 | -- Implementation of `interrupted`
536 | -- Of course we keep closing scopes and releasing resources!
539 | -- Nothing to do except switching to a different scope.
542 | -- We arrived at an interrupt handler and can continue with
543 | -- the replacement pull `q`.
546 | -- In all other cases, there is nothing to do. Note: We don't
547 | -- use a catch-all pattern here to make sure we don't miss any
548 | -- data constructors that might be introduced at a later point.
556 | ||| Runs a `Pull` to completion, eventually producing
557 | ||| a value of type `r`.
558 | |||
559 | ||| Note: In case of an infinite stream, this will loop forever.
560 | ||| In order to cancel the evaluation, consider using
561 | ||| `Async` and racing it with a cancelation thread (for instance,
562 | ||| by waiting for an operating system signal).
570 | ||| Like `pull` but without the possibility of failure. Returns `neutral`
571 | ||| in case the stream was interrupted.
580 | ||| Runs a Pull to completion in the given scope, without
581 | ||| closing the scope. Use this when the pull was generated from
582 | ||| an outer scope that is still in use.