use std::future::IntoFuture;
use std::io::{stdout, Error, Write};
use futures::executor::LocalPool;
use futures::future::LocalBoxFuture;
use futures::Future;
use crate::{Apply, Bind, Functor, Pure};
pub enum IO<'a, A, E> {
Future(LocalBoxFuture<'a, Result<A, E>>),
Error(E),
}
impl<'a, A, E> IO<'a, A, E> {
pub fn from_error(error: E) -> Self {
Self::Error(error)
}
pub fn map<B, F>(self, f: F) -> IO<'a, B, E>
where
A: 'a,
E: 'a,
F: FnOnce(A) -> B + 'a,
{
match self {
Self::Error(error) => IO::from_error(error),
Self::Future(future) => async move {
match future.await {
Err(error) => Err(error),
Ok(result) => Ok(f(result)),
}
}
.into(),
}
}
pub fn map_error<B, F>(self, f: F) -> IO<'a, A, B>
where
A: 'a,
E: 'a,
F: FnOnce(E) -> B + 'a,
{
match self {
Self::Error(error) => IO::from_error(f(error)),
Self::Future(future) => async {
match future.await {
Err(error) => Err(f(error)),
Ok(result) => Ok(result),
}
}
.into(),
}
}
pub fn is_err(&self) -> bool {
match self {
Self::Error(_) => true,
_ => false,
}
}
}
impl<'a, A, E> core::fmt::Debug for IO<'a, A, E> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str(&format!(
"IO<{}, {}>",
std::any::type_name::<A>(),
std::any::type_name::<E>(),
))
}
}
impl<'a, A, E: 'a> IntoFuture for IO<'a, A, E> {
type Output = Result<A, E>;
type IntoFuture = LocalBoxFuture<'a, Result<A, E>>;
fn into_future(self) -> Self::IntoFuture {
match self {
Self::Future(future) => future,
Self::Error(error) => Box::pin(async { Err(error) }),
}
}
}
impl<'a, A, E, F> From<F> for IO<'a, A, E>
where
F: Future<Output = Result<A, E>> + 'a,
{
fn from(future: F) -> Self {
Self::Future(Box::pin(future))
}
}
impl<'a, A: 'a, E: 'a> Bind<'a, A> for IO<'a, A, E> {
type Target<T> = IO<'a, T, E>;
fn bind<B, F>(self, f: F) -> Self::Target<B>
where
F: Fn(A) -> Self::Target<B> + 'a,
{
match self {
Self::Error(error) => <Self::Target<B>>::from_error(error),
Self::Future(future) => async move {
match future.await {
Ok(result) => f(result),
Err(error) => <Self::Target<B>>::from_error(error),
}
.await
}
.into(),
}
}
}
impl<'a, A: 'a, E: 'a> Functor<'a, A> for IO<'a, A, E> {
type Target<T> = IO<'a, T, E>;
fn fmap<B, F>(self, f: F) -> Self::Target<B>
where
F: Fn(A) -> B + 'a,
{
self.map(f)
}
}
impl<'a, A: 'a, E> Pure<A> for IO<'a, A, E> {
fn pure(value: A) -> Self {
async move { Ok(value) }.into()
}
}
impl<'a, A: 'a, E: 'a> Apply<'a, A> for IO<'a, A, E> {
type Target<T> = IO<'a, T, E> where T: 'a;
fn apply<B>(
self,
f: <Self as Apply<'a, A>>::Target<crate::apply::ApplyFn<'a, A, B>>,
) -> <Self as Apply<'a, A>>::Target<B>
where
B: 'a,
{
async move {
match (f.await, self.await) {
(Err(error), _) => Err(error),
(_, Err(error)) => Err(error),
(Ok(func), Ok(arg)) => Ok(func.apply(arg)),
}
}
.into()
}
}
pub fn run_io<'a, A, E>(io: IO<'a, A, E>) -> Result<A, E> {
LocalPool::new().run_until(io.into_future())
}
pub fn putstrln<'a, S: AsRef<str> + 'a>(s: S) -> IO<'a, (), Error> {
async move {
stdout()
.write(format!("{}\n", s.as_ref()).as_bytes())
.map(|_| ())
}
.into()
}