Trait dialectic::Session[][src]

pub trait Session where
    Self: Scoped + Actionable<NextAction = Self::Action> + HasDual<DualSession = Self::Dual>, 
{ type Dual; type Action; fn channel<Tx, Rx>(
        make: impl FnMut() -> (Tx, Rx)
    ) -> (Chan<Self, Tx, Rx>, Chan<Self::Dual, Tx, Rx>)
    where
        Self::Dual: Scoped + Actionable + HasDual,
        Tx: Send + 'static,
        Rx: Send + 'static
, { ... }
fn bichannel<Tx0, Rx0, Tx1, Rx1>(
        make0: impl FnOnce() -> (Tx0, Rx0),
        make1: impl FnOnce() -> (Tx1, Rx1)
    ) -> (Chan<Self, Tx0, Rx1>, Chan<Self::Dual, Tx1, Rx0>)
    where
        Self::Dual: Scoped + Actionable + HasDual,
        Tx0: Send + 'static,
        Rx0: Send + 'static,
        Tx1: Send + 'static,
        Rx1: Send + 'static
, { ... }
fn wrap<Tx, Rx>(tx: Tx, rx: Rx) -> Chan<Self, Tx, Rx>
    where
        Tx: Send + 'static,
        Rx: Send + 'static
, { ... }
fn over<Tx, Rx, T, F, Fut>(
        tx: Tx,
        rx: Rx,
        with_chan: F
    ) -> Over<Tx, Rx, T, Fut>

Notable traits for Over<Tx, Rx, T, Fut>

impl<Tx, Rx, T, Fut> Future for Over<Tx, Rx, T, Fut> where
    Fut: Future<Output = T>, 
type Output = (T, Result<(Tx, Rx), SessionIncomplete<Tx, Rx>>);

    where
        Tx: Send + 'static,
        Rx: Send + 'static,
        F: FnOnce(Chan<Self, Tx, Rx>) -> Fut,
        Fut: Future<Output = T>
, { ... } }

The Session extension trait gives methods to create session-typed channels from session types. These are implemented as static methods on the session type itself.

This trait is already implemented for all valid session types, and cannot be extended by users of this crate.

Examples

use dialectic::prelude::*;
use dialectic_tokio_mpsc as mpsc;

let (c1, c2) = <Session! { send String }>::channel(mpsc::unbounded_channel);
// do something with these channels...

Counterexamples

It is only possible to create a session-typed channel when the session type is valid. The following examples fail, for the reasons described:

  1. The session type send &'a str for a non-static 'a is not 'static, but all session types must be 'static:

    fn something<'a>(_: &'a str) {
        let (c1, c2) = <Session! { send &'a str }>::channel(mpsc::unbounded_channel);
    }
  2. The session type Loop<Continue<1>> is not Scoped, because Continue<1> must occur within two nested Loops to be properly scoped:

    use dialectic::types::{Loop, Continue};
    use dialectic::unary::types::*;
    let (c1, c2) = <Loop<Continue<1>>>::channel(mpsc::unbounded_channel);

    Note that you cannot write out an ill-scoped session type using the Session! macro, because it will throw an error if you try.

  3. The session type loop {} is not Actionable because it is an “unproductive” infinite loop, where no matter how many times you loop, there will never be an available action to perform on the channel:

    let (c1, c2) = <Session! { loop {} }>::channel(mpsc::unbounded_channel);

Associated Types

type Dual[src]

The dual to this session type, i.e. the session type for the other side of this channel.

Every individual session type component has a dual defined by HasDual. This is that type.

type Action[src]

The canonical next channel action for this session type.

For Send, Recv, Offer, Choose, Split, Call, and Done, the next channel action is the session type itself. For Loop, the next channel action is the inside of the loop, with all Continues within it appropriately unrolled by one loop iteration.

This is always the action type defined by Actionable for this session type.

Loading content...

Provided methods

fn channel<Tx, Rx>(
    make: impl FnMut() -> (Tx, Rx)
) -> (Chan<Self, Tx, Rx>, Chan<Self::Dual, Tx, Rx>) where
    Self::Dual: Scoped + Actionable + HasDual,
    Tx: Send + 'static,
    Rx: Send + 'static, 
[src]

Given a closure which generates a uni-directional underlying transport channel, create a pair of dual Chans which communicate over the transport channels resulting from these closures.

By internally wiring together the two directional channels, this function assures that communications over the channels actually follow the session specified.

Examples

use dialectic::prelude::*;
use dialectic_tokio_mpsc as mpsc;

let (c1, c2) = <Session! { /* ... */ }>::channel(mpsc::unbounded_channel);

fn bichannel<Tx0, Rx0, Tx1, Rx1>(
    make0: impl FnOnce() -> (Tx0, Rx0),
    make1: impl FnOnce() -> (Tx1, Rx1)
) -> (Chan<Self, Tx0, Rx1>, Chan<Self::Dual, Tx1, Rx0>) where
    Self::Dual: Scoped + Actionable + HasDual,
    Tx0: Send + 'static,
    Rx0: Send + 'static,
    Tx1: Send + 'static,
    Rx1: Send + 'static, 
[src]

Given two closures, each of which generates a uni-directional underlying transport channel, create a pair of dual Chans which communicate over the transport channels resulting from these closures.

By internally wiring together the two directional channels, this function assures that communications over the channels actually follow the session specified.

Examples

use dialectic::prelude::*;
use dialectic_tokio_mpsc as mpsc;

let (c1, c2) = <Session! { /* ... */ }>::bichannel(
    mpsc::unbounded_channel,
    || mpsc::channel(1),
);

fn wrap<Tx, Rx>(tx: Tx, rx: Rx) -> Chan<Self, Tx, Rx> where
    Tx: Send + 'static,
    Rx: Send + 'static, 
[src]

Given a transmitting and receiving end of an un-session-typed connection, wrap them in a new session-typed channel for the protocol P.

It is expected that the other ends of these connections will be wrapped in a channel with the Dual session type.

Examples

use dialectic::prelude::*;
use dialectic_tokio_mpsc as mpsc;

let (tx, rx) = mpsc::unbounded_channel();
let c = <Session! { /* ... */ }>::wrap(tx, rx);
c.close();

fn over<Tx, Rx, T, F, Fut>(tx: Tx, rx: Rx, with_chan: F) -> Over<Tx, Rx, T, Fut>

Notable traits for Over<Tx, Rx, T, Fut>

impl<Tx, Rx, T, Fut> Future for Over<Tx, Rx, T, Fut> where
    Fut: Future<Output = T>, 
type Output = (T, Result<(Tx, Rx), SessionIncomplete<Tx, Rx>>);
where
    Tx: Send + 'static,
    Rx: Send + 'static,
    F: FnOnce(Chan<Self, Tx, Rx>) -> Fut,
    Fut: Future<Output = T>, 
[src]

Given a closure which runs the session on a channel from start to completion, run that session on the given pair of sending and receiving connections.

Errors

The closure must finish the session P on the channel given to it and drop the finished channel before the future returns. If the channel is dropped before completing P or is not dropped after completing P, a SessionIncomplete error will be returned instead of a channel for Q. The best way to ensure this error does not occur is to call close on the channel before returning from the future, because this statically checks that the session is complete and drops the channel.

Examples

use dialectic::prelude::*;
use dialectic_tokio_mpsc as mpsc;

let (tx, rx) = mpsc::unbounded_channel();
let (output, ends) = <Session! { /* ... */ }>::over(tx, rx, |chan| async move {
    chan.close();
    Ok::<_, mpsc::Error>("Hello!".to_string())
}).await;

assert_eq!(output?, "Hello!");
let (tx, rx) = ends?;

As noted above, an error is thrown when the channel is dropped too early:

use dialectic::SessionIncomplete::BothHalves;
use dialectic::IncompleteHalf::Unfinished;

let (tx, rx) = mpsc::unbounded_channel();
let (_, ends) = <Session! { send String }>::over(tx, rx, |chan| async move {
    Ok::<_, mpsc::Error>(())
}).await;

assert!(matches!(ends, Err(BothHalves { tx: Unfinished(_), rx: Unfinished(_) })));

And likewise, an error is thrown when the channel is not dropped by the end of the future:

use std::sync::{Arc, Mutex};

use dialectic::SessionIncomplete::BothHalves;
use dialectic::IncompleteHalf::Unclosed;

// 🚨 DON'T DO THIS! 🚨
// We'll put the `Chan` here so it outlives the closure
let hold_on_to_chan = Arc::new(Mutex::new(None));
let hold = hold_on_to_chan.clone();

let (tx, rx) = mpsc::unbounded_channel();
let (_, ends) = <Session! { send String }>::over(tx, rx, |chan| async move {
    *hold.lock().unwrap() = Some(chan);
    Ok::<_, mpsc::Error>(())
}).await;

assert!(matches!(ends, Err(BothHalves { tx: Unclosed, rx: Unclosed })));

// 🚨 DON'T DO THIS! 🚨
// Make sure the `Chan` outlives the closure by holding onto it until here
drop(hold_on_to_chan);
Loading content...

Implementors

impl<S: Scoped + Actionable + HasDual> Session for S[src]

type Dual = Self::DualSession

type Action = Self::NextAction

Loading content...