union!
union!
- one macro to rule them all. Combines sync/async results, transforms tuple of results in result of tuple, provides single and multi thread (sync/async) step by step execution of branches and useful shortcut combinators.
Map: |>
expr - value.map(expr)
AndThen: =>
expr - value.and_then(expr),
Then: ->
expr - expr(value)
Dot: >.
expr - value.expr
Or: <|
expr - value.or(expr)
OrElse: <=
expr - value.or_else(expr)
MapErr: !>
expr - value.map_err(expr)
Inspect: ?>
expr - (|value| { expr(value); value })(value)
where value
is the previous value.
Every combinator prefixed by ~
will act as deferred action (all actions will wait until completion in every step and only after move to the next one).
might be one of
map
=> will act as results.map(|(result0, result1, ..)| handler(result0, result1, ..))
and_then
=> will act as results.and_then(|(result0, result1, ..)| handler(result0, result1, ..))
then
=> will act as handler(result0, result1, ..)
or not specified - then Result<(result0, result1, ..), Error> or Option<(result0, result1, ..)> will be returned.
Converts input in series of chained results and joins them step by step.
```rust extern crate union;
use std::error::Error; use union::union;
type Result
fn action_1() -> Result
fn action_2() -> Result
fn main() { let sum = union! { action1(), action2().map(|v| v as u16), action2().map(|v| v as u16 + 1).andthen(|v| Ok(v * 4)), action1().andthen(|_| Err("5".into())).or(Ok(2)), map => |a, b, c, d| a + b + c + d }.expect("Failed to calculate sum");
println!("Calculated: {}", sum);
} ```
Each branch will represent chain of tasks. All branches will be joined using join!
macro and macro will return unpolled
future.
```rust
extern crate union; extern crate futures; extern crate tokio;
use std::error::Error; use union::union_async; use futures::future::{ok, err};
type Result
async fn action1() -> Result
async fn main() { let sum = unionasync! { action1(), action2().andthen(|v| ok(v as u16)), action2().map(|v| v.map(|v| v as u16 + 1)).andthen(|v| ok(v * 4u16)), action1().andthen(|| err("5".into())).orelse(|| ok(2u16)), andthen => |a, b, c, d| ok(a + b + c + d) }.await.expect("Failed to calculate sum");
println!("Calculated: {}", sum);
} ```
To execute several tasks in parallel you could use union_spawn!
(spawnion!
) for sync tasks
and union_async_spawn!
(spasyncion!
) for async tasks. Since union_async
already provides parallel futures execution in one thread, union_async_spawn!
spawns every branch into tokio
executor so they will be evaluated in multi-threaded executor.
union_spawn
spawns one ::std::thread
per each step of each branch (number of branches is the max thread count at the time).
```rust extern crate union;
use std::error::Error; use union::union_spawn;
type Result
fn action_1() -> Result
fn action_2() -> Result
fn main() { // Branches will be executed in parallel let sum = unionspawn! { action1(), action2().map(|v| v as usize), action2().map(|v| v as usize + 1).andthen(|v| Ok(v * 4)), action1().andthen(|| Err("5".into())).or(Ok(2)), map => |a, b, c, d| a + b + c + d }.expect("Failed to calculate sum");
println!("Calculated: {}", sum);
} ```
union_async_spawn!
uses ::tokio::spawn
function to spawn tasks so it should be done inside tokio
runtime
(number of branches is the max count of tokio
tasks at the time).
```rust
extern crate union; extern crate futures; extern crate tokio;
use std::error::Error; use union::unionasyncspawn; use futures::future::{ok, err};
type Result
async fn action_1() -> Result
async fn action_2() -> Result
async fn main() { let sum = unionasyncspawn! { action1(), action2().andthen(|v| ok(v as u16)), action2().map(|v| v.map(|v| v as u16 + 1)).andthen(|v| ok(v * 4u16)), action1().andthen(|| err("5".into())).orelse(|| ok(2u16)), and_then => |a, b, c, d| ok(a + b + c + d) }.await.expect("Failed to calculate sum");
println!("Calculated: {}", sum);
} ```
Using combinators we can rewrite first example like
```rust extern crate union;
use std::error::Error; use union::union;
type Result
fn action_1() -> Result
fn action_2() -> Result
fn main() { let sum = union! { action1(), action2() |> |v| v as u16, action2() |> |v| v as u16 + 1 => |v| Ok(v * 4), action1() => |_| Err("5".into()) <| Ok(2), map => |a, b, c, d| a + b + c + d }.expect("Failed to calculate sum");
println!("Calculated: {}", sum);
} ```
By separating chain in actions, you will make actions wait for completion of all of them in current step before go to the next step.
```rust extern crate union;
use std::error::Error; use union::union;
type Result
fn action_1() -> Result
fn action_2() -> Result
fn main() {
let sum = union! {
action1(),
let result1 = action2() ~|> |v| v as u16 + 1,
action2() ~|> move |v| {
// result_1
now is the result of action_2()
[Ok(1u8)]
if result1.isok() {
v as u16 + 1
} else {
unreachable!()
}
} ~=> move |v| {
// result_1
now is the result of |v| v as u16 + 1
[Ok(2u16)]
if let Ok(result1) = result1 {
Ok(v * 4 + result1)
} else {
unreachable!()
}
},
action1() ~=> |_| Err(5) <| Ok(2),
map => |a, b, c, d| a + b + c + d
}.expect("Failed to calculate sum");
println!("Calculated: {}", sum);
}
```