From eb153d6396fade6e9efbb92e6a12615ae7a13782 Mon Sep 17 00:00:00 2001 From: timofey Date: Sat, 22 Apr 2023 15:30:33 +0000 Subject: [PATCH] ibind/IState --- src/core.rs | 22 +++++---- src/func.rs | 20 ++++++++ src/func/classes/future.rs | 17 +++++++ src/func/classes/lazy.rs | 15 ++++++ src/func/classes/option.rs | 15 ++++++ src/func/classes/result.rs | 15 ++++++ src/func/classes/solo.rs | 15 ++++++ src/func/classes/stackless.rs | 18 ++++++- src/std.rs | 4 +- src/std/atomic/plain.rs | 20 +++++--- src/std/cast.rs | 40 +++++++++++++-- src/std/collections/stack.rs | 93 ++++++++++++++++++++++++++++++++--- src/std/nullable.rs | 23 ++++++--- src/testing.rs | 11 +++++ 14 files changed, 288 insertions(+), 40 deletions(-) diff --git a/src/core.rs b/src/core.rs index cb70876..d5d215b 100644 --- a/src/core.rs +++ b/src/core.rs @@ -19,20 +19,21 @@ pub trait Context { } /// Failure yielded by [`Origin`]. +#[derive(Debug)] pub enum ResolutionError { Lookup(L), Parse(P), } -/// Result yielded by [`Origin`]. -pub type ResolutionResult<'a, Ctx, A> = Result< - Rc, - ResolutionError< - ::LookupError<'a>, - <>::Fctr as Factory<'a, Ctx>>::ParseError, - >, +/// See [`ResolutionResult`]. +pub type ResolutionFailure<'a, Ctx, A> = ResolutionError< + ::LookupError<'a>, + <>::Fctr as Factory<'a, Ctx>>::ParseError, >; +/// Result yielded by [`Origin`]. +pub type ResolutionResult<'a, Ctx, A> = Result, ResolutionFailure<'a, Ctx, A>>; + /// Helper alias for [`WeakFunctor::F`] of [`Context::T`]. pub type Wrapped<'a, Ctx, A> = <::T as WeakFunctor>::F<'a, A>; @@ -98,8 +99,11 @@ pub trait Mentionable<'a, Ctx: 'a + Context>: 'a + Serializable { } /// Short-hand for the type of values returned by [`Resolver::resolve`]. -pub type HashResolution<'a, Ctx> = - Wrapped<'a, Ctx, Result<(Vec, Rc>), ::LookupError<'a>>>; +pub type HashResolution<'a, Ctx> = Wrapped< + 'a, + Ctx, + Result<(Vec, Rc>), ::LookupError<'a>>, +>; /// Value accepted by [`Resolver::resolve`]. Includes index to make it order-sensitive. #[derive(Clone, Copy, Debug)] diff --git a/src/func.rs b/src/func.rs index bf91e58..da47149 100644 --- a/src/func.rs +++ b/src/func.rs @@ -147,6 +147,14 @@ pub trait Applicative: Functor + ApplicativeSeq + ApplicativeLA2 + ApplicativeTu } } +/// Represents iteration state. +pub enum IState { + /// Loop running. + Pending(A), + /// Loop finished. + Done(B), +} + /// Equivalent of Haskell's `Monad`. /// /// @@ -159,6 +167,18 @@ pub trait Monad: Applicative { where Self: 'a; + /// Kleisli category composition special case used to represent loops. + /// Included for optimisation and clarity. + /// Generally, [`Monad::bind`] should be enough implement it. + /// See [`classes::stackless::StacklessClass::ibind`] for a generic, though less-than ideal, blanket implementation. + /// On practice, you shouldn't be using [`Monad::bind`]/[`Applicative::pure`]/[`Functor::fmap`] here. + fn ibind<'a, A: 'a, B: 'a>( + a: A, + f: impl 'a + FnMut(A) -> Self::F<'a, IState>, + ) -> Self::F<'a, B> + where + Self: 'a; + /// Equivalent of Haskell's `join`. fn join<'a, A: 'a>(ffa: Self::F<'a, Self::F<'a, A>>) -> Self::F<'a, A> where diff --git a/src/func/classes/future.rs b/src/func/classes/future.rs index bb66f8a..0d127c1 100644 --- a/src/func/classes/future.rs +++ b/src/func/classes/future.rs @@ -84,6 +84,23 @@ impl Monad for FutureClass { Box::pin(async { f(fa.await).await }) } + fn ibind<'a, A: 'a, B: 'a>( + mut a: A, + mut f: impl 'a + FnMut(A) -> Self::F<'a, IState>, + ) -> Self::F<'a, B> + where + Self: 'a, + { + Box::pin(async move { + loop { + match f(a).await { + IState::Pending(next_a) => a = next_a, + IState::Done(b) => return b, + }; + } + }) + } + fn join<'a, A: 'a>(ffa: Self::F<'a, Self::F<'a, A>>) -> Self::F<'a, A> { Box::pin(async { ffa.await.await }) } diff --git a/src/func/classes/lazy.rs b/src/func/classes/lazy.rs index 8990a4c..123b4f5 100644 --- a/src/func/classes/lazy.rs +++ b/src/func/classes/lazy.rs @@ -83,6 +83,21 @@ impl Monad for LazyClass { Box::new(|| f(fa())()) } + fn ibind<'a, A: 'a, B: 'a>( + mut a: A, + mut f: impl 'a + FnMut(A) -> Self::F<'a, IState>, + ) -> Self::F<'a, B> + where + Self: 'a, + { + Box::new(move || loop { + match f(a)() { + IState::Pending(next_a) => a = next_a, + IState::Done(b) => return b, + }; + }) + } + fn join<'a, A: 'a>(ffa: Self::F<'a, Self::F<'a, A>>) -> Self::F<'a, A> { Box::new(|| ffa()()) } diff --git a/src/func/classes/option.rs b/src/func/classes/option.rs index cb72511..bdd2732 100644 --- a/src/func/classes/option.rs +++ b/src/func/classes/option.rs @@ -77,6 +77,21 @@ impl Monad for OptionClass { f(fa?) } + fn ibind<'a, A: 'a, B: 'a>( + mut a: A, + mut f: impl 'a + FnMut(A) -> Self::F<'a, IState>, + ) -> Self::F<'a, B> + where + Self: 'a, + { + loop { + match f(a)? { + IState::Pending(next_a) => a = next_a, + IState::Done(b) => return Self::pure(b), + }; + } + } + fn join<'a, A: 'a>(ffa: Self::F<'a, Self::F<'a, A>>) -> Self::F<'a, A> { ffa? } diff --git a/src/func/classes/result.rs b/src/func/classes/result.rs index 3c6375e..bc6367c 100644 --- a/src/func/classes/result.rs +++ b/src/func/classes/result.rs @@ -77,6 +77,21 @@ impl Monad for ResultClass { f(fa?) } + fn ibind<'a, A: 'a, B: 'a>( + mut a: A, + mut f: impl 'a + FnMut(A) -> Self::F<'a, IState>, + ) -> Self::F<'a, B> + where + Self: 'a, + { + loop { + match f(a)? { + IState::Pending(next_a) => a = next_a, + IState::Done(b) => return Self::pure(b), + }; + } + } + fn join<'a, A: 'a>(ffa: Self::F<'a, Self::F<'a, A>>) -> Self::F<'a, A> { ffa? } diff --git a/src/func/classes/solo.rs b/src/func/classes/solo.rs index 6140290..348721b 100644 --- a/src/func/classes/solo.rs +++ b/src/func/classes/solo.rs @@ -77,6 +77,21 @@ impl Monad for SoloClass { f(fa) } + fn ibind<'a, A: 'a, B: 'a>( + mut a: A, + mut f: impl 'a + FnMut(A) -> Self::F<'a, IState>, + ) -> Self::F<'a, B> + where + Self: 'a, + { + loop { + match f(a) { + IState::Pending(next_a) => a = next_a, + IState::Done(b) => return b, + }; + } + } + fn join<'a, A: 'a>(ffa: Self::F<'a, Self::F<'a, A>>) -> Self::F<'a, A> { ffa } diff --git a/src/func/classes/stackless.rs b/src/func/classes/stackless.rs index 3b36eac..5575506 100644 --- a/src/func/classes/stackless.rs +++ b/src/func/classes/stackless.rs @@ -117,7 +117,7 @@ impl<'a, A: 'a> From for Stackless<'a, A> { } } -struct StacklessClass; +pub struct StacklessClass; impl WeakFunctor for StacklessClass { type F<'a, A: 'a> = Stackless<'a, A>; @@ -221,6 +221,22 @@ impl Monad for StacklessClass { { fa.bind(f) } + + fn ibind<'a, A: 'a, B: 'a>( + a: A, + mut f: impl 'a + FnMut(A) -> Self::F<'a, IState>, + ) -> Self::F<'a, B> + where + Self: 'a, + { + Self::pure(a).bind(move |a| { + f(a).bind(|state| match state { + IState::Pending(a) => Self::ibind(a, f), + IState::Done(b) => Self::pure(b), + }) + }) + } + fn join<'a, A: 'a>(ffa: Self::F<'a, Self::F<'a, A>>) -> Self::F<'a, A> where Self::F<'a, A>: 'a, diff --git a/src/std.rs b/src/std.rs index 2e68497..37575a4 100644 --- a/src/std.rs +++ b/src/std.rs @@ -344,11 +344,11 @@ impl Display for Address { } pub trait ExtSerializable: Serializable { - fn to_vec(&self) -> Vec; + fn bytes(&self) -> Vec; } impl ExtSerializable for S { - fn to_vec(&self) -> Vec { + fn bytes(&self) -> Vec { let mut vec = Vec::new(); self.serialize(&mut vec); vec diff --git a/src/std/atomic/plain.rs b/src/std/atomic/plain.rs index d462202..c424324 100644 --- a/src/std/atomic/plain.rs +++ b/src/std/atomic/plain.rs @@ -3,12 +3,12 @@ use std::fmt::Display; use crate::std::atomic::*; #[derive(Clone)] -struct Plain { - pub data: Vec, +pub struct Plain { + data: Vec, } #[derive(Debug)] -enum PlainParseError {} +pub enum PlainParseError {} impl Display for PlainParseError { fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -28,9 +28,7 @@ impl Atomic for Plain { type ParseError = PlainParseError; fn deserialize(deserializer: &mut dyn Deserializer) -> Result { - Ok(Plain { - data: deserializer.read_all().into(), - }) + Ok(Plain::from_slice(deserializer.read_all())) } fn unexpected_tail(tail: &[u8]) -> Self::ParseError { @@ -41,15 +39,21 @@ impl Atomic for Plain { } } +impl Plain { + pub fn from_slice(slice: &[u8]) -> Self { + Plain { data: slice.into() } + } +} + #[cfg(test)] mod tests { - use crate::std::*; use super::*; + use crate::std::*; #[test] fn test_plain() -> Result<(), PlainParseError> { let plain = Plain::parse_slice(b"slice")?; - assert_eq!(plain.to_vec().as_slice(), b"slice"); + assert_eq!(plain.bytes().as_slice(), b"slice"); Ok(()) } } diff --git a/src/std/cast.rs b/src/std/cast.rs index 4521116..b9b7bcb 100644 --- a/src/std/cast.rs +++ b/src/std/cast.rs @@ -5,9 +5,13 @@ struct CastResolver<'a, Ctx: 'a + Context> { points: Vec>>, } +#[derive(Debug)] pub enum CastError<'a> { Typeless(TypelessError<'a>), - AddressIndexOutOfBounds(usize), + AddressIndexOutOfBounds { + index: usize, + length: usize, + }, AddressPointMismatch { expected: Hash, received: Hash, @@ -15,6 +19,30 @@ pub enum CastError<'a> { }, } +impl<'a> Display for CastError<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + CastError::Typeless(typeless_error) => { + f.write_fmt(format_args!("typeless cast error: {}", typeless_error)) + } + CastError::AddressIndexOutOfBounds { index, length } => f.write_fmt(format_args!( + "cast index out of bound: {}>={}", + index, length + )), + CastError::AddressPointMismatch { + expected, + received, + index, + } => f.write_fmt(format_args!( + "address mismatch at index {}: {}>={}", + index, + hex::encode(expected), + hex::encode(received), + )), + } + } +} + impl<'a, Ctx: 'a + Context> Resolver<'a, Ctx> for CastResolver<'a, Ctx> where Ctx::LookupError<'a>: From>, @@ -23,7 +51,11 @@ where let point = match self.points.get(address.index) { Some(point) => point, None => { - return Ctx::T::pure(Err(CastError::AddressIndexOutOfBounds(address.index).into())); + return Ctx::T::pure(Err(CastError::AddressIndexOutOfBounds { + index: address.index, + length: self.points.len(), + } + .into())); } }; if point.point != address.point { @@ -40,7 +72,7 @@ where let resolver: Rc> = Rc::new(CastResolver { points: mentionable.points(), }); - Ok((mentionable.to_vec(), resolver)) + Ok((mentionable.bytes(), resolver)) } Err(error) => Err(match error { ResolutionError::Lookup(lookup_error) => lookup_error, @@ -60,7 +92,7 @@ where { pub fn cast>(&self, factory: A::Fctr) -> CastResult<'a, Ctx, A> { factory.parse_slice( - &self.to_vec(), + &self.bytes(), Rc::new(CastResolver { points: self.points(), }), diff --git a/src/std/collections/stack.rs b/src/std/collections/stack.rs index 728c2fa..be3f01c 100644 --- a/src/std/collections/stack.rs +++ b/src/std/collections/stack.rs @@ -4,7 +4,7 @@ use crate::std::*; pub struct StackNode<'a, Ctx: 'a + Context, A: Mentionable<'a, Ctx>> { pub rest: Stack<'a, Ctx, A>, - pub element: A, + pub element: Rc, } type Stack<'a, Ctx, A> = Nullable<'a, Ctx, StackNode<'a, Ctx, A>>; @@ -90,12 +90,14 @@ impl<'a, Ctx: 'a + Context, A: Mentionable<'a, Ctx>> Factory<'a, Ctx> return Err(StackParseError::Point(ppe)); } }; - let element = match self.element_factory.deserialize(deserializer, resolver) { - Ok(element) => element, - Err(epe) => { - return Err(StackParseError::Element(epe)); - } - }; + let element = Rc::new( + match self.element_factory.deserialize(deserializer, resolver) { + Ok(element) => element, + Err(epe) => { + return Err(StackParseError::Element(epe)); + } + }, + ); Ok(StackNode { rest, element }) } @@ -104,10 +106,17 @@ impl<'a, Ctx: 'a + Context, A: Mentionable<'a, Ctx>> Factory<'a, Ctx> } } +pub type StackFaiure<'a, Ctx, A> = ResolutionFailure<'a, Ctx, StackNode<'a, Ctx, A>>; + +pub type StackVecResult<'a, Ctx, A> = Result>, StackFaiure<'a, Ctx, A>>; + +pub type StackVecWrapped<'a, Ctx, A> = Wrapped<'a, Ctx, StackVecResult<'a, Ctx, A>>; + pub trait ExtStack<'a, Ctx: 'a + Context, A: Mentionable<'a, Ctx>>: Mentionable<'a, Ctx> { fn empty(factory: A::Fctr) -> Self; fn f(factory: A::Fctr) -> Self::Fctr; fn add(self, element: A) -> Self; + fn vec(self) -> StackVecWrapped<'a, Ctx, A>; } impl<'a, Ctx: 'a + Context, A: Mentionable<'a, Ctx>> ExtStack<'a, Ctx, A> for Stack<'a, Ctx, A> { @@ -121,9 +130,77 @@ impl<'a, Ctx: 'a + Context, A: Mentionable<'a, Ctx>> ExtStack<'a, Ctx, A> for St Nullable::NotNull( StackNode { rest: self, - element, + element: Rc::new(element), } .into(), ) } + fn vec(self) -> StackVecWrapped<'a, Ctx, A> { + Ctx::T::ibind((vec![], self), |(mut vec, stack)| match stack { + Nullable::Null(_) => Ctx::T::pure(IState::Done(Ok(vec))), + Nullable::NotNull(point) => Ctx::T::fmap( + |resolved| { + let node = match resolved { + Ok(node) => node, + Err(error) => { + return IState::Done(Err(error)); + } + }; + vec.push(node.element.clone()); + IState::Pending((vec, node.rest.clone())) + }, + point.resolve(), + ), + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::std::atomic::plain::*; + use crate::std::atomic::*; + use crate::testing::*; + + type T = Stack<'static, TestContext, Plain>; + + fn unstack(stack: &T) -> Result>, StackFaiure<'static, TestContext, Plain>> { + Ok(stack + .clone() + .vec()? + .iter() + .map(|plain| plain.bytes()) + .collect()) + } + + fn make_stack() -> T { + let stack: T = Stack::empty(Plain::f()); + + let stack: T = stack.add(Plain::from_slice(b"A0")); + let stack: T = stack.add(Plain::from_slice(b"B1")); + let stack: T = stack.add(Plain::from_slice(b"C2")); + + stack + } + + fn validate_stack(stack: &T) { + let mut vec: Vec> = unstack(stack).unwrap(); + vec.reverse(); + assert_eq!(vec, vec![b"A0".to_vec(), b"B1".to_vec(), b"C2".to_vec()]); + } + + #[test] + fn test_stack() -> Result<(), StackFaiure<'static, TestContext, Plain>> { + let stack: T = make_stack(); + + validate_stack(&stack); + + let stack: T = TypelessMentionable::from_typed(stack.into()) + .cast(Stack::f(Plain::f())) + .expect("cast failed"); + + validate_stack(&stack); + + Ok(()) + } } diff --git a/src/std/nullable.rs b/src/std/nullable.rs index 64d81ae..e5cbbbc 100644 --- a/src/std/nullable.rs +++ b/src/std/nullable.rs @@ -65,13 +65,13 @@ impl<'a, Ctx: 'a + Context, A: Mentionable<'a, Ctx>> Factory<'a, Ctx> deserializer: &mut dyn Deserializer, resolver: std::rc::Rc>, ) -> ParseResult<'a, Ctx, Self> { - let point = deserializer.read_n_const::()?; + let mut addresses = Addresses::start(); let factory = self.factory.clone(); - Ok(if point == HASH_ZEROS { + let address = addresses.next(deserializer)?; + Ok(if address.point == HASH_ZEROS { Nullable::Null(factory) } else { - let mut addresses = Addresses::start(); - Nullable::NotNull(addresses.next_point(deserializer, resolver, factory)?) + Nullable::NotNull(Point::from_point(address, factory, resolver)) }) } @@ -86,10 +86,8 @@ impl<'a, Ctx: 'a + Context, A: Mentionable<'a, Ctx>> Nullable<'a, Ctx, Nullable< Nullable::Null(nullable_factory) => { let NullableFactory { factory } = nullable_factory; Ctx::T::pure(Ok(Rc::new(Nullable::Null(factory.clone())))) - }, - Nullable::NotNull(point) => { - point.resolve() - }, + } + Nullable::NotNull(point) => point.resolve(), } } } @@ -99,3 +97,12 @@ impl<'a, Ctx: 'a + Context, A: Mentionable<'a, Ctx>> NullableFactory<'a, Ctx, A> Self { factory } } } + +impl<'a, Ctx: 'a + Context, A: Mentionable<'a, Ctx>> Clone for Nullable<'a, Ctx, A> { + fn clone(&self) -> Self { + match self { + Self::Null(factory) => Self::Null(factory.clone()), + Self::NotNull(point) => Self::NotNull(point.clone()), + } + } +} diff --git a/src/testing.rs b/src/testing.rs index 48a19ca..9139bb5 100644 --- a/src/testing.rs +++ b/src/testing.rs @@ -3,6 +3,7 @@ use std::fmt::Display; use crate::core::*; use crate::func::*; +use crate::std::cast::CastError; use sha2::{Digest, Sha256}; pub struct TestContext; @@ -10,6 +11,7 @@ pub struct TestContext; #[derive(Debug)] pub enum TestLookupError<'a> { Typeless(TypelessError<'a>), + Cast(CastError<'a>), EmptyResolverAccess(Address), } @@ -19,12 +21,21 @@ impl<'a> From> for TestLookupError<'a> { } } +impl<'a> From> for TestLookupError<'a> { + fn from(value: CastError<'a>) -> Self { + Self::Cast(value) + } +} + impl<'a> Display for TestLookupError<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { TestLookupError::Typeless(typeless_error) => { f.write_fmt(format_args!("typeless lookup failure: {}", typeless_error)) } + TestLookupError::Cast(cast_error) => { + f.write_fmt(format_args!("cast failure: {}", cast_error)) + } TestLookupError::EmptyResolverAccess(address) => f.write_fmt(format_args!( "accessed an empty resolved at address {}.", address