Struct naan::io::IO

source ·
pub struct IO<T>(_);
Expand description

Lazy managed I/O

This structure represents the concept of any & all side-effecting computations, wrapping lazy IO in a monad.

Value Proposition

Managed IO allows you to wrap procedural code in an ergonomic declarative interface.

For some problems, it can be valuable to express a series of computations and side-effects as a “pipe” that data will eventually flow through, rather than a sequence of imperative statements.

Use of IO doesn’t come with any inherent performance gains or costs (due to IO not relying on heap allocations or dynamic dispatch) and is a mostly stylistic choice.

Initializing an IO

Values already in scope that need to be wrapped in IO can be lifted with IO::pure.

Using IO to defer computations until some later time can be done with IO::suspend.

Executing an IO

IO<A> (and the types returned by map_/bind_/apply_) implement the IOLike trait, which provides a function fn exec(self) -> A

IO in parameter and return positions

Use impl IOLike<A> instead of concrete types like IO<A> or Suspend<A>.

use core::ops::Add;

use naan::io;
use naan::prelude::*;

fn add12(n: usize) -> usize {
  n + 12
}

// Prefer:
fn foo(io: impl IOLike<usize>) -> impl IOLike<usize> {
  io.map_(add12)
}

// Over:
fn bar(io: IO<usize>) -> io::Map<fn(usize) -> usize, usize, usize, IO<usize>> {
  io.map_(add12)
}

Most of the types you’ll interact with are “surrogate” types that are still conceptually “an IO,” but represent different transformations:

IO has been designed with no heap allocations or dynamic dispatch, and accomplishes this by progressively building a stack of structs that store data & work to do.

This stack is collapsed when you call [IOLike.exec].

Note, that IOLike provides map_, apply_, and bind_, so for most usecases you don’t need to think or worry about concrete types.

use core::ops::Add;
use std::cell::Cell;

use naan::prelude::*;

fn get_number_from_network() -> impl IOLike<usize> {
  IO::suspend(|()| 1111)
}

let x = Cell::new(0usize);
let lazy = IO::suspend(|()| "123").map_(|s| usize::from_str_radix(s, 10).unwrap())
                                  .map_(|n| {
                                    x.set(x.get() + 1);
                                    n
                                  })
                                  .map_((|a, b| a + b).curry()) // 123 + _
                                  .apply_(get_number_from_network()); // 123 + 1111

assert_eq!(x.get(), 0);
assert_eq!(lazy.exec(), 1234);
assert_eq!(x.get(), 1);

Implementations§

source§

impl<T> IO<T>

source

pub fn pure(t: T) -> Self

Lift an eager value of type T to IO<T>.

source

pub fn suspend<F>(f: F) -> Suspend<F>where F: F1Once<(), Ret = T>,

Store a lazy computation

Trait Implementations§

source§

impl<A> Equiv for IO<A>

§

type To = IO<A>

The target that Self is conceptually equivalent to
source§

impl<A> IOLike<A> for IO<A>

source§

fn exec(self) -> A

Execute this lazy computation

Auto Trait Implementations§

§

impl<T> RefUnwindSafe for IO<T>where T: RefUnwindSafe,

§

impl<T> Send for IO<T>where T: Send,

§

impl<T> Sync for IO<T>where T: Sync,

§

impl<T> Unpin for IO<T>where T: Unpin,

§

impl<T> UnwindSafe for IO<T>where T: UnwindSafe,

Blanket Implementations§

source§

impl<T> Any for Twhere T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<I, A> ApplicativeSurrogate<IO, A> for Iwhere I: Equiv<To = IO<A>> + IOLike<A>,

source§

fn pure(a: A) -> IO<A>

Lift A to F<A>
source§

impl<I, AB, TofA> ApplySurrogate<IO, AB, TofA> for Iwhere I: Equiv<To = IO<AB>> + IOLike<AB>,

§

type ApplyOutput = Apply<A, B, AB, TofA, I>

Type returned by apply_ that is conceptually the same as F::T<B>. Read more
source§

fn apply_<A, B>(self, a: TofA) -> Apply<A, B, AB, TofA, I>where AB: F1Once<A, Ret = B>,

Apply the function A -> B contained in Self (F<A -> B>) to an instance of F<A> to get F<B>.
source§

impl<T> Borrow<T> for Twhere T: ?Sized,

const: unstable · source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere T: ?Sized,

const: unstable · source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

const: unstable · source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<I, A> FunctorSurrogate<IO, A> for Iwhere I: Equiv<To = IO<A>> + IOLike<A>,

§

type Output = Map<AB, A, B, I>

Type yielded by fmap that is akin to F::T<B>. Read more
source§

fn map_<AB, B>(self, f: AB) -> Map<AB, A, B, I>where AB: F1<A, Ret = B>,

Use a function from A -> B to transform something akin to F<A> to something akin to F<B>.
source§

impl<T, U> Into<U> for Twhere U: From<T>,

const: unstable · source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<I, A> MonadSurrogate<IO, A> for Iwhere I: Equiv<To = IO<A>> + IOLike<A>,

§

type BindOutput = Bind<AMB, A, B, I>

Type yielded by bind_ that isn’t exactly M::T<B>, but is conceptually Equivalent. Read more
source§

fn bind_<B, AMB>( self, f: AMB ) -> <I as MonadSurrogate<IO, A>>::BindOutput<B, AMB>where AMB: F1<A, Ret = <IO as HKT1>::T<B>>,

Use a function from A -> M<B> to transform something akin to M<A> to something akin to M<B>.
source§

impl<F, A, TF, T> Sequence<F, A, TF> for T

source§

fn sequence<Ap>(self) -> Ap::T<F::T<A>>where Self: Sized + Traversable<F, Ap::T<A>, A, TF> + Foldable<F, Ap::T<A>>, Ap: HKT1, Ap::T<A>: Applicative<Ap, A> + ApplyOnce<Ap, A>, Ap::T<TF>: Applicative<Ap, TF> + ApplyOnce<Ap, TF>, Ap::T<F::T<A>>: Applicative<Ap, F::T<A>> + ApplyOnce<Ap, F::T<A>>, F: HKT1<T<Ap::T<A>> = Self>,

source§

impl<T, U> TryFrom<U> for Twhere U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
const: unstable · source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
const: unstable · source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.