Function bicoro::subroutine

source ·
pub fn subroutine<'a, Input, Output, ChildInput, ChildOutput, OnInput, OnOutput, Result>(
    on_input: OnInput,
    on_output: OnOutput,
    child: Coroutine<'a, ChildInput, ChildOutput, Result>
) -> Coroutine<'a, Input, Output, Result>where
    OnInput: Fn() -> Coroutine<'a, Input, Output, ChildInput> + Send + 'a,
    OnOutput: Fn(ChildOutput) -> Coroutine<'a, Input, Output, ()> + Send + 'a,
    ChildOutput: Send,
    Result: Send,
Expand description

Runs a subroutine and converts it to the hosted type

As the child subroutine may have different inputs and outputs you need to specify how to convert between these worlds. This is done by providing functions that yield coroutines that can be ran in the ‘parent’ context. One that is ran whenever the child needs an input and one that is ran when the child produces an output

These can simply proxy through to the parents context, or perform more processing. Both still retain inputs and outputs so they may call a parents recieve multiple times

use bicoro::*;

/* Types in this example are written explicitly to highlight what they are
   It should be possible to use more compact forms
*/

// Reads a value, echos it, then returns 0u8
let child: Coroutine<i64,i64,u8> = receive().and_then(send).and_then(|()| result(0u8));

// an example where we require two inputs from the parent context, to one on the child
// in this case we add the two i32's together to form an i64
let on_input = | | -> Coroutine<i32,_,i64> {
    bind(receive(),|a| {
        map(receive(),move |b| {
            (a as i64+b as i64)
        })
    })
};

// parent expects strings on it's output channel so we convert
// note: type gaps used here as it must line up with the parent context types
// also means it's possible to read from the parent context when producing output
let on_output = | o:i64 | -> Coroutine<_,String,()> {
    send(o.to_string())
};

// run_child maps the child routine into the parenst 'world'
// we can see the type of this is the parent coroutine
let parent : Coroutine<i32,String,u8> = subroutine(on_input,on_output,child);
Examples found in repository?
examples/fsm-input-driven.rs (line 62)
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
pub fn main() {
    let turnstile = create();

    // we can run a child routine inside this routine, with the provided functiosn to convert
    // the inputs and outputs. Result remains the same.
    let mut composed = send(Output::StdOut(
        "You are stopped by a turnstile!\r\n".to_string(),
    ))
    .and_then(|()| send(Output::Flush))
    .and_then(|()| subroutine(needs_input, on_output, turnstile));

    // This is the main loop. Notice it's really only concerned
    // with handling inputs and outputs of the termninal.
    loop {
        match run_step(composed) {
            StepResult::Done(_) => unreachable!(), // just for this case, as its a non-exiting coroutine
            StepResult::Yield { output, next } => {
                match output {
                    Output::StdOut(o) => print!("{}", o),
                    Output::StdErr(e) => print!("{}", e),
                    Output::Flush => std::io::stdout().flush().unwrap(),
                }
                composed = *next;
            }
            StepResult::Next(fun) => {
                // Prompts only appear when needed here
                let mut buf = String::new();
                std::io::stdin().read_line(&mut buf).unwrap();
                let input = Input(buf);
                composed = fun(input);
            }
        }
    }
}