Buffered transforms. A buffered transform represents a method for insertingcomment
converting a stream of input
s to a stream of output
s. However,
input
s can also be buffered, i.e. you can feed input
s to the
transform and pull out output
s later. There is no requirement
that 1 input produces exactly 1 output. It is common that multiple
input values are needed to construct a single output, and vice
versa.
Buffered transforms serve as a general method for working with
streams of data and flexibly composing mappings from input
s to
output
s. The buffering aspect supports asynchronous programming
interfaces. Parsers and printers throughout Biocaml are
implemented with this module whenever possible.
Often mappings need to account for errors, e.g. an input string
cannot be converted to an integer. Several methods below
explicitly support buffered transforms where the output type is a
Result.t
.
module Biocaml_transform:
sig
type ('input, 'output)
t
'input
s to
'output
s.exception Feeding_stopped_transform of string
feed
is called on a transform after it
has been stop
ped.val make : ?name:string ->
feed:('input -> unit) ->
next:(bool -> [ `end_of_stream | `not_ready | `output of 'output ]) ->
unit -> ('input, 'output) t
make ~feed ~next ()
creates a transform that can be
fed with feed
and read from with next
.
feed input
should store input
in a buffer, which is
presumably a shared state also available to next
.next stopped
should remove values from the buffer, convert it
to an `output
and return this output, or return `not_ready
if
there are not enough buffered inputs to create an output value, or
return `end_of_stream
if the buffer has been stopped, as
determined by the supplied argument, and there is no more
input.Result.t
.
name
an optional name for the transform that will be used in
error messages.val feed : ('input, 'output) t -> 'input -> unit
feed t i
stores i
into the buffered transform.Feeding_stopped_transform
name
if called on a t
that has been stop
ped.val next : ('input, 'output) t ->
[ `end_of_stream | `not_ready | `output of 'output ]
next t
returns an output value if possible, `not_ready
if t
needs to be fed with more input before it can produce an output,
or `end_of_stream
if t
has been stopped and has no more
data.val stop : ('input, 'output) t -> unit
stop t
declares t
to be stopped, which means subsequent calls to:
feed t _
will raise Feeding_stopped_transform
. Feeding
a stopped transform is not allowed.next t
will eventually return `end_of_stream
, not
necessarily the immediate next call as there may still be
buffered values available for output.val identity : ?name:string -> unit -> ('a, 'a) t
identity ()
returns a transform that simply returns its inputs
as outputs without modification (it can be seen as a
simple Queue.t
).val of_function : ?name:string -> ('a -> 'b) -> ('a, 'b) t
of_function f
is like identity ()
but the transform outputs
are passed to the function f
.val to_stream_fun : ('input, 'output) t -> 'input Stream.t -> 'output Stream.t
to_stream_fun t
returns a function f
that behaves like
t
but the inputs and outputs are on standard OCaml streams.val in_channel_strings_to_stream : ?buffer_size:int ->
Pervasives.in_channel ->
(string, 'output) t -> 'output Stream.t
in_channel_strings_to_stream ic t
returns a stream of 'output
s
given a transform t
that knows how to produce 'output
s from
strings. The strings are read from the in_channel.val stream_to_out_channel : 'input Stream.t ->
('input, string) t -> Pervasives.out_channel -> unit
stream_to_out_channel xs t oc
consumes a stream of 'input
s
using t
to transform them into strings, which are then written
on the out_channel oc
.
Buffered transforms are mutable and one should not expect nice
mathematical properties from composing them. The intention here is
to provide building blocks that allow the creation of more complex
transforms from simpler ones. Only the final resultant transform
should be used. Feeding/reading the transforms being composed is
likely to lead to violations of the stated behavior of the above
operations.
val on_input : ('b, 'c) t -> f:('a -> 'b) -> ('a, 'c) t
on_input f t
returns a transform that converts its inputs with
f
and feeds the results to t
.val on_output : ('a, 'b) t -> f:('b -> 'c) -> ('a, 'c) t
on_output t f
returns a transform that behaves like t
except
the outputs are first converted by f
.val mix : ('a1, 'b1) t ->
('a2, 'b2) t ->
('a1 * 'a2, [ `both of 'b1 * 'b2 | `left of 'b1 | `right of 'b2 ])
t
val filter_compose : ('il, 'ol) t ->
('ir, 'our) t ->
destruct:('ol -> [ `bypass of 'filtered | `transform of 'ir ]) ->
reconstruct:([ `bypassed of 'filtered | `transformed of 'our ] -> 'result) ->
('il, 'result) t
val split_and_merge : ('il, 'ol) t ->
('ir, 'our) t ->
split:('input -> [ `left of 'il | `right of 'ir ]) ->
merge:([ `left of 'ol | `right of 'our ] -> 'output) ->
('input, 'output) t
split_and_merge t u ~split ~merge
returns a transform whose
input is split using split
, passing the result either to t
or u
,
and then the outputs of t
and u
are combined using merge
. There
is no guarantee about the order in which the inputs are fed to t
and
u
(it depends on the buffering done by the individual input
transforms).
Operations analogous to those above, but for transforms whose
output types are Result.t
s.
val make_result : ?name:string ->
feed:('input -> unit) ->
next:(bool ->
[ `end_of_stream | `not_ready | `output of ('a, 'b) Core.Std.Result.t ]) ->
unit -> ('input, ('a, 'b) Core.Std.Result.t) t
Biocaml_transform.make
but the output is a Result.t
. Also,
Biocaml_transform.stop
is automatically called when an error occurs.val on_ok : ('input, ('ok, 'error) Core.Std.Result.t) t ->
f:('ok -> 'still_ok) ->
('input, ('still_ok, 'error) Core.Std.Result.t) t
on_output
but on the successful part of the output.val on_error : ('input, ('ok, 'error) Core.Std.Result.t) t ->
f:('error -> 'another_errror) ->
('input, ('ok, 'another_errror) Core.Std.Result.t) t
on_output
but on the erroneous part of the output.val compose_results : on_error:([ `left of 'error_left | `right of 'error_right ] -> 'error) ->
('input_left, ('middle, 'error_left) Core.Std.Result.t) t ->
('middle, ('output_right, 'error_right) Core.Std.Result.t)
t ->
('input_left, ('output_right, 'error) Core.Std.Result.t) t
compose_results t u
is like Biocaml_transform.compose
but for transforms returning
Result.t
s. The on_error
function specifies how errors in t
or u
should be converted into those in the resultant
transform.
val compose_results_merge_error : ('a, ('b, 'el) Core.Std.Result.t) t ->
('b, ('d, 'er) Core.Std.Result.t) t ->
('a, ('d, [ `left of 'el | `right of 'er ]) Core.Std.Result.t)
t
val compose_result_left : ('input_left, ('middle, 'error) Core.Std.Result.t) t ->
('middle, 'output_right) t ->
('input_left, ('output_right, 'error) Core.Std.Result.t) t
class type[['input, 'output]]
object_t =object
method next :[ `end_of_stream | `not_ready | `output of 'output ]
method feed :'input -> unit
method stop :unit
end
val make_general : ?name:string ->
next:(unit -> [ `end_of_stream | `not_ready | `output of 'output ]) ->
feed:('input -> unit) ->
stop:(unit -> unit) -> unit -> ('input, 'output) t
end