From 99280f62bf3f8ed390d0091811bf60ce9a9629c9 Mon Sep 17 00:00:00 2001 From: Heman Gandhi Date: Mon, 7 Oct 2019 22:31:27 -0400 Subject: [PATCH 01/15] Traverse? --- fp-core/src/hkt.rs | 3 +++ fp-core/src/traverse.rs | 17 +++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 fp-core/src/traverse.rs diff --git a/fp-core/src/hkt.rs b/fp-core/src/hkt.rs index 70a8a72..23ff18d 100644 --- a/fp-core/src/hkt.rs +++ b/fp-core/src/hkt.rs @@ -17,6 +17,9 @@ macro_rules! derive_hkt { }; } +// A nice way of saying G> purely through HKT. +pub type HktInHkt = >::Target>>::Target; + derive_hkt!(Option); derive_hkt!(Vec); diff --git a/fp-core/src/traverse.rs b/fp-core/src/traverse.rs new file mode 100644 index 0000000..a420983 --- /dev/null +++ b/fp-core/src/traverse.rs @@ -0,0 +1,17 @@ +use crate::applicative::Applicative; +use crate::foldable::Foldable; +use crate::functor::Functor; +use crate::hkt::{HktInHkt, HKT}; + +// Re-statement of the Haskell definition (http://hackage.haskell.org/package/base-4.12.0.0/docs/Data-Traversable.html) +pub trait Traversable: Functor + Foldable { + // A is the source type, F the functor, and AFB a natural transformation + // from A into F B. Pursuant to Cats in Scala (https://www.scala-exercises.org/cats/traverse), + // A would be a User, F a future, and Self would be a list. + fn traverse( + traverser: AFB, + traversed: >::Target, + ) -> HktInHkt + where + AFB: Box >::Target>; +} From 2e0f8a0a4c859e4bf0e3ac364a9ba2abfd27127e Mon Sep 17 00:00:00 2001 From: Heman Gandhi Date: Mon, 7 Oct 2019 23:13:07 -0400 Subject: [PATCH 02/15] Better traverse, with todos --- fp-core/src/applicative.rs | 4 ++-- fp-core/src/hkt.rs | 1 + fp-core/src/pure.rs | 2 +- fp-core/src/traverse.rs | 17 +++++++++++++++-- 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/fp-core/src/applicative.rs b/fp-core/src/applicative.rs index 44e065a..797e439 100644 --- a/fp-core/src/applicative.rs +++ b/fp-core/src/applicative.rs @@ -1,6 +1,6 @@ use crate::apply::Apply; use crate::pure::Pure; -pub trait Applicative: Apply + Pure {} +pub trait Applicative: Apply + Pure {} -impl Applicative for Option {} +impl Applicative for Option {} diff --git a/fp-core/src/hkt.rs b/fp-core/src/hkt.rs index 23ff18d..50207c4 100644 --- a/fp-core/src/hkt.rs +++ b/fp-core/src/hkt.rs @@ -18,6 +18,7 @@ macro_rules! derive_hkt { } // A nice way of saying G> purely through HKT. +// TODO(after declarative macros stabilize): make a macro to define arbitrary lists of these. pub type HktInHkt = >::Target>>::Target; derive_hkt!(Option); diff --git a/fp-core/src/pure.rs b/fp-core/src/pure.rs index 4dc3df1..9903a3a 100644 --- a/fp-core/src/pure.rs +++ b/fp-core/src/pure.rs @@ -4,7 +4,7 @@ pub trait Pure: HKT { fn of(c: Self::Current) -> Self::Target; } -impl Pure for Option { +impl Pure for Option { fn of(a: A) -> Self::Target { Some(a) } diff --git a/fp-core/src/traverse.rs b/fp-core/src/traverse.rs index a420983..65c41c9 100644 --- a/fp-core/src/traverse.rs +++ b/fp-core/src/traverse.rs @@ -4,14 +4,27 @@ use crate::functor::Functor; use crate::hkt::{HktInHkt, HKT}; // Re-statement of the Haskell definition (http://hackage.haskell.org/package/base-4.12.0.0/docs/Data-Traversable.html) +// TODO: who actually is "self"? They should be different to maintain that we're emitting an HKT, +// so self is arguably Self for all A. Or is self really just "traversed"? pub trait Traversable: Functor + Foldable { // A is the source type, F the functor, and AFB a natural transformation - // from A into F B. Pursuant to Cats in Scala (https://www.scala-exercises.org/cats/traverse), - // A would be a User, F a future, and Self would be a list. + // from A into F. Pursuant to Cats in Scala (https://www.scala-exercises.org/cats/traverse), + // A would be a User, F a future, and Self would be a list. AFB would be the sort of "user + // promise" function in the example. Self, perhaps, is just a witness for now. fn traverse( + self, traverser: AFB, traversed: >::Target, ) -> HktInHkt where + F: Applicative, AFB: Box >::Target>; } + +pub fn sequenceA(wrong_order: HktInHkt) -> HktInHkt +where + T: Traversable, + F: Applicative, +{ + wrong_order.traverse(|x| x, wrong_order) +} From 203fc71b592452ef92e9969632de1165d2f4b299 Mon Sep 17 00:00:00 2001 From: Heman Gandhi Date: Tue, 8 Oct 2019 22:34:00 -0400 Subject: [PATCH 03/15] impl Traversable for Option --- fp-core/src/traverse.rs | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/fp-core/src/traverse.rs b/fp-core/src/traverse.rs index 65c41c9..7f1b4ee 100644 --- a/fp-core/src/traverse.rs +++ b/fp-core/src/traverse.rs @@ -3,28 +3,40 @@ use crate::foldable::Foldable; use crate::functor::Functor; use crate::hkt::{HktInHkt, HKT}; -// Re-statement of the Haskell definition (http://hackage.haskell.org/package/base-4.12.0.0/docs/Data-Traversable.html) -// TODO: who actually is "self"? They should be different to maintain that we're emitting an HKT, -// so self is arguably Self for all A. Or is self really just "traversed"? +// Re-statement of the Haskell definition +// (http://hackage.haskell.org/package/base-4.12.0.0/docs/Data-Traversable.html) pub trait Traversable: Functor + Foldable { // A is the source type, F the functor, and AFB a natural transformation // from A into F. Pursuant to Cats in Scala (https://www.scala-exercises.org/cats/traverse), // A would be a User, F a future, and Self would be a list. AFB would be the sort of "user - // promise" function in the example. Self, perhaps, is just a witness for now. - fn traverse( - self, - traverser: AFB, - traversed: >::Target, - ) -> HktInHkt + // promise" function in the example. + fn traverse(&self, traverser: AFB) -> HktInHkt + // (Self is a HKT so this works out) where F: Applicative, AFB: Box >::Target>; } +// Re-statement of the Haskell function +// (http://hackage.haskell.org/package/base-4.12.0.0/docs/Data-Traversable.html) pub fn sequenceA(wrong_order: HktInHkt) -> HktInHkt where T: Traversable, F: Applicative, { - wrong_order.traverse(|x| x, wrong_order) + wrong_order.traverse(|x| x) +} + +impl Traversable for Option { + fn traverse(&self, traverser: AFB) -> HktInHkt + // (Self is a HKT so this works out) + where + F: Applicative, + AFB: Box >::Target>, + { + match &self { + Option::None => Option::None, + Option::Some(a) => Option::Some(traverser(a)), + } + } } From 78b47d49a99022c17c220df3be3d8427fee33229 Mon Sep 17 00:00:00 2001 From: Heman Gandhi Date: Tue, 8 Oct 2019 22:44:42 -0400 Subject: [PATCH 04/15] impl Traversable for Result --- fp-core/src/traverse.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/fp-core/src/traverse.rs b/fp-core/src/traverse.rs index 7f1b4ee..d6b3ce3 100644 --- a/fp-core/src/traverse.rs +++ b/fp-core/src/traverse.rs @@ -40,3 +40,18 @@ impl Traversable for Option { } } } + +impl Traversable for Result { + fn traverse(&self, traverser: AFB) -> HktInHkt + // (Self is a HKT so this works out) + where + F: Applicative, + AFB: Box >::Target>, + { + match &self { + Result::Err(e) => Result::Err(e), + Result::Ok(a) => Result::Ok(traverser(a)), + } + } +} + From d291472c641942cc56778132b34cdb0f0b2ab0db Mon Sep 17 00:00:00 2001 From: Heman Gandhi Date: Wed, 9 Oct 2019 23:16:17 -0400 Subject: [PATCH 05/15] vec instances all over the place --- fp-core/src/applicative.rs | 1 + fp-core/src/apply.rs | 6 ++++++ fp-core/src/functor.rs | 10 ++++++++++ fp-core/src/pure.rs | 6 ++++++ fp-core/src/traverse.rs | 16 ++++++++++++++++ 5 files changed, 39 insertions(+) diff --git a/fp-core/src/applicative.rs b/fp-core/src/applicative.rs index f63c490..13d1731 100644 --- a/fp-core/src/applicative.rs +++ b/fp-core/src/applicative.rs @@ -4,5 +4,6 @@ use crate::pure::Pure; pub trait Applicative: Apply + Pure {} impl Applicative for Option {} +impl Applicative for Vec {} impl Applicative for Result {} diff --git a/fp-core/src/apply.rs b/fp-core/src/apply.rs index 2b043db..be2dba3 100644 --- a/fp-core/src/apply.rs +++ b/fp-core/src/apply.rs @@ -18,3 +18,9 @@ impl Apply for Result { self.and_then(|v| f.map(|z| z(v))) } } + +impl Apply for Vec { + fn ap(self, f: Applicator) -> >::Target { + self.iter().flat_map(|v| f.iter().map(|z| z(v))).collect() + } +} diff --git a/fp-core/src/functor.rs b/fp-core/src/functor.rs index a5b7730..1d09c67 100644 --- a/fp-core/src/functor.rs +++ b/fp-core/src/functor.rs @@ -16,6 +16,16 @@ impl Functor for Option { } } +impl Functor for Vec { + fn fmap(self, f: F) -> Self::Target + where + // A is Self::Current + F: FnOnce(A) -> B, + { + self.iter().map(f).collect() + } +} + impl Functor for Result { fn fmap(self, f: F) -> Self::Target where diff --git a/fp-core/src/pure.rs b/fp-core/src/pure.rs index bbead1a..3f2a09f 100644 --- a/fp-core/src/pure.rs +++ b/fp-core/src/pure.rs @@ -10,6 +10,12 @@ impl Pure for Option { } } +impl Pure for Vec { + fn of(a: A) -> Self::Target { + vec![a] + } +} + impl Pure for Result { fn of(a: A) -> Self::Target { Ok(a) diff --git a/fp-core/src/traverse.rs b/fp-core/src/traverse.rs index d6b3ce3..273331c 100644 --- a/fp-core/src/traverse.rs +++ b/fp-core/src/traverse.rs @@ -41,6 +41,22 @@ impl Traversable for Option { } } +impl Traversable for Vec { + fn traverse(&self, traverser: AFB) -> HktInHkt + where + F: Applicative, + AFB: Box >::Target>, + { + // Interestingly only uses that Self is a monoid. + let acc = F::of(vec![]); + for item in self { + let t = traverser(item); + acc = acc.apply(|acc_list| acc_list + vec![t]); + } + return acc; + } +} + impl Traversable for Result { fn traverse(&self, traverser: AFB) -> HktInHkt // (Self is a HKT so this works out) From a3d28a2abfe2eac9c85c44d9b986b40d7ea0484a Mon Sep 17 00:00:00 2001 From: Heman Gandhi Date: Sat, 12 Oct 2019 15:48:12 -0400 Subject: [PATCH 06/15] an example --- fp-examples/src/traverse_example.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 fp-examples/src/traverse_example.rs diff --git a/fp-examples/src/traverse_example.rs b/fp-examples/src/traverse_example.rs new file mode 100644 index 0000000..bbfa574 --- /dev/null +++ b/fp-examples/src/traverse_example.rs @@ -0,0 +1,20 @@ +use fp_core::traverse::*; + +fn div_tuple(tpl: (f32, f32)) -> Option { + match tpl { + (_, 0) => Option::None, + (x, y) => Option::Some(x / y), + } +} + +#[test] +fn traverse_example() { + let will_be_some_quotients = vec![(2, 1), (4, 2), (6, 3)]; + assert_eq!( + will_be_some_quotients.traverse(div_tuple), + Option::Some(vec![2, 2, 2]) + ); + + let will_not_be_some_quotients = vec![(2, 1), (4, 2), (6, 3), (45, 0)]; + assert_eq!(will_not_be_some_quotients.traverse(div_tuple), Option::None); +} From 1dfdddcf256a673201c00ecb893713ddc21a0bfd Mon Sep 17 00:00:00 2001 From: Heman Gandhi Date: Sun, 29 Dec 2019 14:30:01 -0500 Subject: [PATCH 07/15] A fix for all the bugs --- fp-core/src/apply.rs | 26 ++++++++++++++++---------- fp-core/src/functor.rs | 11 +++++------ fp-core/src/monad.rs | 6 +++--- fp-core/src/pure.rs | 8 ++++---- 4 files changed, 28 insertions(+), 23 deletions(-) diff --git a/fp-core/src/apply.rs b/fp-core/src/apply.rs index 26248a8..b7e7ab9 100644 --- a/fp-core/src/apply.rs +++ b/fp-core/src/apply.rs @@ -1,26 +1,32 @@ use crate::functor::Functor; use crate::hkt::HKT; -type Applicator> = >::Current) -> B>>>::Target; +// This is the type for a container of functions we can apply over. +// In haskell, we say `apply::f a -> f (a -> b) -> f b`, however, in rust +// most `f (a -> b)` also need a size for the `a -> b`, so we box it. +// Furthermore, in rust, the HKT trait knows `a`: ` as HKT>::Current` is `A`. +// Saying this in every place we'd have to was looking unwieldy, so this type does it for us. +// `Applicator>` is `Option T>>`. +type Applicator> = >::Current) -> B>>>::Target; -pub trait Apply: Functor + HKT>::Current) -> B>> { - fn ap(self, f: Applicator) -> >::Target; +pub trait Apply: Functor + HKT>::Current) -> B>> { + fn ap(self, f: &Applicator) -> >::Target; } impl Apply for Option { - fn ap(self, f: Applicator) -> >::Target { - self.and_then(|v| f.map(|z| z(v))) + fn ap(self, f: &Applicator) -> >::Target { + self.and_then(|v| f.as_ref().map(|z| z(&v))) } } -impl Apply for Result { - fn ap(self, f: Applicator) -> >::Target { - self.and_then(|v| f.map(|z| z(v))) +impl Apply for Result { + fn ap(self, f: &Applicator) -> >::Target { + self.and_then(|v| f.as_ref().map(|z| z(&v)).or_else(move |e| Result::Err(*e))) } } impl Apply for Vec { - fn ap(self, f: Applicator) -> >::Target { - self.iter().flat_map(|v| f.iter().map(|z| z(v))).collect() + fn ap(self, f: &Applicator) -> >::Target { + self.into_iter().flat_map(move |v| f.into_iter().map(move |z| z(&v))).collect() } } diff --git a/fp-core/src/functor.rs b/fp-core/src/functor.rs index 1d09c67..c440989 100644 --- a/fp-core/src/functor.rs +++ b/fp-core/src/functor.rs @@ -3,14 +3,14 @@ use crate::hkt::HKT; pub trait Functor: HKT { fn fmap(self, f: F) -> Self::Target where - F: FnOnce(Self::Current) -> B; + F: Fn(Self::Current) -> B; } impl Functor for Option { fn fmap(self, f: F) -> Self::Target where // A is Self::Current - F: FnOnce(A) -> B, + F: Fn(A) -> B, { self.map(f) } @@ -19,10 +19,9 @@ impl Functor for Option { impl Functor for Vec { fn fmap(self, f: F) -> Self::Target where - // A is Self::Current - F: FnOnce(A) -> B, + F: Fn(>::Current) -> B, { - self.iter().map(f).collect() + self.into_iter().map(f).collect::>() } } @@ -30,7 +29,7 @@ impl Functor for Result { fn fmap(self, f: F) -> Self::Target where // A is Self::Current - F: FnOnce(A) -> B, + F: Fn(A) -> B, { self.map(f) } diff --git a/fp-core/src/monad.rs b/fp-core/src/monad.rs index 2e18204..3892daa 100644 --- a/fp-core/src/monad.rs +++ b/fp-core/src/monad.rs @@ -1,8 +1,8 @@ use crate::applicative::Applicative; use crate::chain::Chain; -pub trait Monad: Chain + Applicative {} +pub trait Monad: Chain + Applicative {} -impl Monad for Option {} +impl Monad for Option {} -impl Monad for Result {} +impl Monad for Result {} diff --git a/fp-core/src/pure.rs b/fp-core/src/pure.rs index 3f2a09f..16c07ce 100644 --- a/fp-core/src/pure.rs +++ b/fp-core/src/pure.rs @@ -1,11 +1,11 @@ use crate::hkt::HKT; pub trait Pure: HKT { - fn of(c: Self::Current) -> Self::Target; + fn of(c: A) -> Self::Target; } impl Pure for Option { - fn of(a: A) -> Self::Target { + fn of(a: A) -> >::Target { Some(a) } } @@ -16,8 +16,8 @@ impl Pure for Vec { } } -impl Pure for Result { - fn of(a: A) -> Self::Target { +impl Pure for Result { + fn of(a: A) -> >::Target { Ok(a) } } From 2ba503c14b7d0628cd6db3864c8000673a861a84 Mon Sep 17 00:00:00 2001 From: Heman Gandhi Date: Sun, 29 Dec 2019 14:55:08 -0500 Subject: [PATCH 08/15] propagate copy necesity and try passing applicator refs --- fp-core/src/applicative.rs | 4 +++- fp-core/src/apply.rs | 2 ++ fp-core/src/monad.rs | 2 +- fp-examples/src/applicative_example.rs | 6 ++++-- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/fp-core/src/applicative.rs b/fp-core/src/applicative.rs index 13d1731..ab0b7e7 100644 --- a/fp-core/src/applicative.rs +++ b/fp-core/src/applicative.rs @@ -6,4 +6,6 @@ pub trait Applicative: Apply + Pure {} impl Applicative for Option {} impl Applicative for Vec {} -impl Applicative for Result {} +// Note on trait bound: look in apply.rs with the +// impl Apply for Result +impl Applicative for Result {} diff --git a/fp-core/src/apply.rs b/fp-core/src/apply.rs index b7e7ab9..e28c987 100644 --- a/fp-core/src/apply.rs +++ b/fp-core/src/apply.rs @@ -19,6 +19,8 @@ impl Apply for Option { } } +// TODO(when as_deref is stable): remove the Copy restruction and replace the +// `or_else` with `as_deref_err`. impl Apply for Result { fn ap(self, f: &Applicator) -> >::Target { self.and_then(|v| f.as_ref().map(|z| z(&v)).or_else(move |e| Result::Err(*e))) diff --git a/fp-core/src/monad.rs b/fp-core/src/monad.rs index 3892daa..b3215e4 100644 --- a/fp-core/src/monad.rs +++ b/fp-core/src/monad.rs @@ -5,4 +5,4 @@ pub trait Monad: Chain + Applicative {} impl Monad for Option {} -impl Monad for Result {} +impl Monad for Result {} diff --git a/fp-examples/src/applicative_example.rs b/fp-examples/src/applicative_example.rs index 1b18c0f..256eef0 100644 --- a/fp-examples/src/applicative_example.rs +++ b/fp-examples/src/applicative_example.rs @@ -5,13 +5,15 @@ mod example { #[test] fn applicative_example() { - let x = Option::of(1).ap(Some(Box::new(|x| x + 1))); + let applicator = Some(Box::new(move |x: &i32| x + 1)); + let x = Option::of(1).ap(&applicator); assert_eq!(x, Some(2)); } #[test] fn applicative_example_on_result() { - let x = Result::<_, ()>::of(1).ap(Ok(Box::new(|x| x + 1))); + let applicator = Ok(Box::new(move |x: &i32| x + 1)); + let x = Result::<_, ()>::of(1).ap(&applicator); assert_eq!(x, Ok(2)); } } From fdfd4344da2e85804aea8e3e8b4a546e4edabba0 Mon Sep 17 00:00:00 2001 From: Heman Gandhi Date: Fri, 10 Jan 2020 00:07:04 -0500 Subject: [PATCH 09/15] Tests pass: it was just a type hint --- fp-core/src/apply.rs | 2 +- fp-examples/src/applicative_example.rs | 8 ++++---- fp-examples/src/monad_example.rs | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/fp-core/src/apply.rs b/fp-core/src/apply.rs index e28c987..be1d140 100644 --- a/fp-core/src/apply.rs +++ b/fp-core/src/apply.rs @@ -7,7 +7,7 @@ use crate::hkt::HKT; // Furthermore, in rust, the HKT trait knows `a`: ` as HKT>::Current` is `A`. // Saying this in every place we'd have to was looking unwieldy, so this type does it for us. // `Applicator>` is `Option T>>`. -type Applicator> = >::Current) -> B>>>::Target; +pub type Applicator> = >::Current) -> B>>>::Target; pub trait Apply: Functor + HKT>::Current) -> B>> { fn ap(self, f: &Applicator) -> >::Target; diff --git a/fp-examples/src/applicative_example.rs b/fp-examples/src/applicative_example.rs index 256eef0..596b54d 100644 --- a/fp-examples/src/applicative_example.rs +++ b/fp-examples/src/applicative_example.rs @@ -5,15 +5,15 @@ mod example { #[test] fn applicative_example() { - let applicator = Some(Box::new(move |x: &i32| x + 1)); - let x = Option::of(1).ap(&applicator); + let applicator: Applicator> = Some(Box::new(move |x: &i32| x + 1)); + let x = Option::::of(1).ap(&applicator); assert_eq!(x, Some(2)); } #[test] fn applicative_example_on_result() { - let applicator = Ok(Box::new(move |x: &i32| x + 1)); - let x = Result::<_, ()>::of(1).ap(&applicator); + let applicator: Applicator> = Ok(Box::new(move |x: &i32| x + 1)); + let x = Result::::of(1).ap(&applicator); assert_eq!(x, Ok(2)); } } diff --git a/fp-examples/src/monad_example.rs b/fp-examples/src/monad_example.rs index 1035143..6bf9772 100644 --- a/fp-examples/src/monad_example.rs +++ b/fp-examples/src/monad_example.rs @@ -5,13 +5,13 @@ mod example { #[test] fn monad_example() { - let x = Option::of(1).chain(|x| Some(x + 1)); + let x = Option::::of(1).chain(|x| Some(x + 1)); assert_eq!(x, Some(2)); } #[test] fn monad_example_on_result() { - let x = Result::<_, ()>::of(1).chain(|x| Ok(x + 1)); + let x = Result::::of(1).chain(|x| Ok(x + 1)); assert_eq!(x, Ok(2)); } } From 0f7fd4416cb1aedd1c63b40f57a42ad351341f01 Mon Sep 17 00:00:00 2001 From: Heman Gandhi Date: Fri, 10 Jan 2020 22:26:13 -0500 Subject: [PATCH 10/15] now with all the errors --- fp-core/src/empty.rs | 12 ++++++++++ fp-core/src/foldable.rs | 34 ++++++++++++++++++++++++++ fp-core/src/lib.rs | 1 + fp-core/src/monoid.rs | 4 ++++ fp-core/src/semigroup.rs | 29 ++++++++++++++++++++++ fp-core/src/traverse.rs | 52 +++++++++++++++++++++------------------- fp-examples/src/main.rs | 1 + 7 files changed, 108 insertions(+), 25 deletions(-) diff --git a/fp-core/src/empty.rs b/fp-core/src/empty.rs index 30f7d78..707a2a3 100644 --- a/fp-core/src/empty.rs +++ b/fp-core/src/empty.rs @@ -37,3 +37,15 @@ impl Empty for String { "".to_string() } } + +impl Empty for Option { + fn empty() -> Option { + Option::None + } +} + +impl Empty for Result { + fn empty() -> Result { + Result::Err(E::empty()) + } +} diff --git a/fp-core/src/foldable.rs b/fp-core/src/foldable.rs index 8f8b376..887c2a2 100644 --- a/fp-core/src/foldable.rs +++ b/fp-core/src/foldable.rs @@ -1,6 +1,8 @@ use crate::hkt::HKT; use crate::monoid::Monoid; +use std::fmt::Debug; + // Cheating: all HKT instances exist for any B, // so HKT here isn't about Self having any meaning, // it's about folding on some Self -- the HKT lets us @@ -47,3 +49,35 @@ impl Foldable for Vec { self.iter().rev().fold(b, |x, y| fa(y, x)) } } + +impl Foldable for Option { + fn reduce(self, b: B, fa: F) -> B + where + F: Fn(B, &A) -> B, + { + self.as_ref().and_then(|v| Option::Some(fa(b, v))).unwrap() + } + + fn reduce_right(self, b: B, fa: F) -> B + where + F: Fn(&A, B) -> B, + { + self.as_ref().and_then(|v| Option::Some(fa(v, b))).unwrap() + } +} + +impl Foldable for Result { + fn reduce(self, b: B, fa: F) -> B + where + F: Fn(B, &A) -> B, + { + self.as_ref().and_then(|v| Result::Ok(fa(b, v))).unwrap() + } + + fn reduce_right(self, b: B, fa: F) -> B + where + F: Fn(&A, B) -> B, + { + self.as_ref().and_then(|v| Result::Ok(fa(v, b))).unwrap() + } +} diff --git a/fp-core/src/lib.rs b/fp-core/src/lib.rs index 9e80cd4..432ddfa 100644 --- a/fp-core/src/lib.rs +++ b/fp-core/src/lib.rs @@ -16,3 +16,4 @@ pub mod monoid; pub mod pure; pub mod semigroup; pub mod setoid; +pub mod traverse; diff --git a/fp-core/src/monoid.rs b/fp-core/src/monoid.rs index 9e403d5..a96f6a8 100644 --- a/fp-core/src/monoid.rs +++ b/fp-core/src/monoid.rs @@ -1,9 +1,13 @@ use crate::empty::Empty; use crate::semigroup::Semigroup; +use std::fmt::Debug; + pub trait Monoid: Empty + Semigroup {} impl Monoid for i32 {} impl Monoid for i64 {} impl Monoid for Vec {} impl Monoid for String {} +impl Monoid for Option {} +impl Monoid for Result {} diff --git a/fp-core/src/semigroup.rs b/fp-core/src/semigroup.rs index 0f96b44..6b2adc5 100644 --- a/fp-core/src/semigroup.rs +++ b/fp-core/src/semigroup.rs @@ -1,3 +1,7 @@ +use crate::empty::Empty; + +use std::fmt::Debug; + pub trait Semigroup { fn combine(self, other: Self) -> Self; } @@ -27,3 +31,28 @@ impl Semigroup for String { format!("{}{}", self, other) } } + +impl Semigroup for Option +where + A: Semigroup, +{ + fn combine(self, other: Self) -> Self { + if self.is_some() && other.is_some() { + return Option::Some(self.unwrap().combine(other.unwrap())); + } + Option::None + } +} + +impl Semigroup for Result +where + A: Semigroup, + E: Empty + Debug, +{ + fn combine(self, other: Self) -> Self { + if self.is_ok() && other.is_ok() { + return Result::Ok(self.unwrap().combine(other.unwrap())); + } + Result::Err(E::empty()) + } +} diff --git a/fp-core/src/traverse.rs b/fp-core/src/traverse.rs index 273331c..1736c28 100644 --- a/fp-core/src/traverse.rs +++ b/fp-core/src/traverse.rs @@ -2,72 +2,74 @@ use crate::applicative::Applicative; use crate::foldable::Foldable; use crate::functor::Functor; use crate::hkt::{HktInHkt, HKT}; +use crate::pure::Pure; + +use std::fmt::Debug; // Re-statement of the Haskell definition // (http://hackage.haskell.org/package/base-4.12.0.0/docs/Data-Traversable.html) -pub trait Traversable: Functor + Foldable { +pub trait Traversable: Functor + Foldable { // A is the source type, F the functor, and AFB a natural transformation // from A into F. Pursuant to Cats in Scala (https://www.scala-exercises.org/cats/traverse), // A would be a User, F a future, and Self would be a list. AFB would be the sort of "user // promise" function in the example. - fn traverse(&self, traverser: AFB) -> HktInHkt + fn traverse(&self, traverser: Box) -> HktInHkt // (Self is a HKT so this works out) where - F: Applicative, - AFB: Box >::Target>; + F: Applicative + Applicative<>::Target>, + AFB: Fn(A) -> >::Target; } // Re-statement of the Haskell function // (http://hackage.haskell.org/package/base-4.12.0.0/docs/Data-Traversable.html) pub fn sequenceA(wrong_order: HktInHkt) -> HktInHkt where - T: Traversable, - F: Applicative, + T: Traversable + HKT<>::Target>, + F: Applicative + HKT<>::Target>, { wrong_order.traverse(|x| x) } -impl Traversable for Option { - fn traverse(&self, traverser: AFB) -> HktInHkt +impl Traversable for Option { + fn traverse(&self, traverser: Box) -> HktInHkt // (Self is a HKT so this works out) where - F: Applicative, - AFB: Box >::Target>, + F: Applicative + Applicative>, + AFB: Fn(A) -> >::Target, { match &self { - Option::None => Option::None, - Option::Some(a) => Option::Some(traverser(a)), + Option::None => >>::of(Option::None), + Option::Some(a) => F::of(Option::Some(traverser(*a))), } } } -impl Traversable for Vec { - fn traverse(&self, traverser: AFB) -> HktInHkt +impl Traversable for Vec { + fn traverse(&self, traverser: Box) -> HktInHkt where - F: Applicative, - AFB: Box >::Target>, + F: Applicative + Applicative>, + AFB: Fn(A) -> >::Target, { // Interestingly only uses that Self is a monoid. let acc = F::of(vec![]); for item in self { - let t = traverser(item); - acc = acc.apply(|acc_list| acc_list + vec![t]); + let t = traverser(*item); + acc = acc.ap(|acc_list| acc_list + vec![t]); } return acc; } } -impl Traversable for Result { - fn traverse(&self, traverser: AFB) -> HktInHkt +impl Traversable for Result { + fn traverse(&self, traverser: Box) -> HktInHkt // (Self is a HKT so this works out) where - F: Applicative, - AFB: Box >::Target>, + F: Applicative + Applicative>, + AFB: Fn(A) -> >::Target, { match &self { - Result::Err(e) => Result::Err(e), - Result::Ok(a) => Result::Ok(traverser(a)), + Result::Err(e) => F::of(Result::Err(e)), + Result::Ok(a) => F::of(Result::Ok(traverser(*a))), } } } - diff --git a/fp-examples/src/main.rs b/fp-examples/src/main.rs index 89d18e5..6acf95c 100644 --- a/fp-examples/src/main.rs +++ b/fp-examples/src/main.rs @@ -34,6 +34,7 @@ mod referential_transparency_example; mod semigroup_example; mod setoid_example; mod side_effects_example; +mod traverse_example; mod type_signature_example; fn main() { From 95d49ee515b2e612a897bcb25504c4c8fd1df716 Mon Sep 17 00:00:00 2001 From: Heman Gandhi Date: Fri, 10 Jan 2020 23:28:39 -0500 Subject: [PATCH 11/15] one parameter traverse --- fp-core/src/traverse.rs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/fp-core/src/traverse.rs b/fp-core/src/traverse.rs index 1736c28..4a1742b 100644 --- a/fp-core/src/traverse.rs +++ b/fp-core/src/traverse.rs @@ -2,13 +2,12 @@ use crate::applicative::Applicative; use crate::foldable::Foldable; use crate::functor::Functor; use crate::hkt::{HktInHkt, HKT}; -use crate::pure::Pure; use std::fmt::Debug; // Re-statement of the Haskell definition // (http://hackage.haskell.org/package/base-4.12.0.0/docs/Data-Traversable.html) -pub trait Traversable: Functor + Foldable { +pub trait Traversable: Functor + Foldable { // A is the source type, F the functor, and AFB a natural transformation // from A into F. Pursuant to Cats in Scala (https://www.scala-exercises.org/cats/traverse), // A would be a User, F a future, and Self would be a list. AFB would be the sort of "user @@ -17,34 +16,33 @@ pub trait Traversable: Functor + Foldable { // (Self is a HKT so this works out) where F: Applicative + Applicative<>::Target>, - AFB: Fn(A) -> >::Target; + AFB: Fn(>::Current) -> >::Target; } // Re-statement of the Haskell function // (http://hackage.haskell.org/package/base-4.12.0.0/docs/Data-Traversable.html) pub fn sequenceA(wrong_order: HktInHkt) -> HktInHkt where - T: Traversable + HKT<>::Target>, + T: Traversable + HKT<>::Target>, F: Applicative + HKT<>::Target>, { wrong_order.traverse(|x| x) } -impl Traversable for Option { +impl Traversable for Option { fn traverse(&self, traverser: Box) -> HktInHkt - // (Self is a HKT so this works out) where - F: Applicative + Applicative>, + F: Applicative + Applicative<>::Target>, AFB: Fn(A) -> >::Target, { match &self { - Option::None => >>::of(Option::None), + Option::None => F::of(Option::None), Option::Some(a) => F::of(Option::Some(traverser(*a))), } } } -impl Traversable for Vec { +impl Traversable for Vec { fn traverse(&self, traverser: Box) -> HktInHkt where F: Applicative + Applicative>, @@ -60,9 +58,8 @@ impl Traversable for Vec { } } -impl Traversable for Result { +impl Traversable for Result { fn traverse(&self, traverser: Box) -> HktInHkt - // (Self is a HKT so this works out) where F: Applicative + Applicative>, AFB: Fn(A) -> >::Target, From eb71a3868f57efd131561600d8cea21a4e5ba874 Mon Sep 17 00:00:00 2001 From: Heman Gandhi Date: Sat, 11 Jan 2020 10:55:39 -0500 Subject: [PATCH 12/15] temp: last attempt at traverse that's not sequence --- fp-core/src/traverse.rs | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/fp-core/src/traverse.rs b/fp-core/src/traverse.rs index 4a1742b..2dec7f5 100644 --- a/fp-core/src/traverse.rs +++ b/fp-core/src/traverse.rs @@ -7,15 +7,17 @@ use std::fmt::Debug; // Re-statement of the Haskell definition // (http://hackage.haskell.org/package/base-4.12.0.0/docs/Data-Traversable.html) -pub trait Traversable: Functor + Foldable { +pub trait Traversable: Functor + Foldable +where + F: Applicative + Applicative<>::Target>, +{ // A is the source type, F the functor, and AFB a natural transformation // from A into F. Pursuant to Cats in Scala (https://www.scala-exercises.org/cats/traverse), // A would be a User, F a future, and Self would be a list. AFB would be the sort of "user // promise" function in the example. - fn traverse(&self, traverser: Box) -> HktInHkt + fn traverse(&self, traverser: Box) -> HktInHkt // (Self is a HKT so this works out) where - F: Applicative + Applicative<>::Target>, AFB: Fn(>::Current) -> >::Target; } @@ -23,16 +25,19 @@ pub trait Traversable: Functor + Foldable { // (http://hackage.haskell.org/package/base-4.12.0.0/docs/Data-Traversable.html) pub fn sequenceA(wrong_order: HktInHkt) -> HktInHkt where - T: Traversable + HKT<>::Target>, - F: Applicative + HKT<>::Target>, + T: Traversable + HKT<>::Target>, + F: Applicative + Applicative<>::Target>, + HktInHkt: Traversable { - wrong_order.traverse(|x| x) + Traversable::::traverse(&wrong_order, Box::new(|x| x)) } -impl Traversable for Option { - fn traverse(&self, traverser: Box) -> HktInHkt +impl Traversable for Option +where + F: Applicative + Applicative>, +{ + fn traverse(&self, traverser: Box) -> HktInHkt where - F: Applicative + Applicative<>::Target>, AFB: Fn(A) -> >::Target, { match &self { @@ -42,10 +47,12 @@ impl Traversable for Option { } } -impl Traversable for Vec { - fn traverse(&self, traverser: Box) -> HktInHkt +impl Traversable for Vec +where + F: Applicative + Applicative>, +{ + fn traverse(&self, traverser: Box) -> HktInHkt where - F: Applicative + Applicative>, AFB: Fn(A) -> >::Target, { // Interestingly only uses that Self is a monoid. @@ -58,10 +65,12 @@ impl Traversable for Vec { } } -impl Traversable for Result { - fn traverse(&self, traverser: Box) -> HktInHkt +impl Traversable for Result +where + F: Applicative + Applicative>, +{ + fn traverse(&self, traverser: Box) -> HktInHkt where - F: Applicative + Applicative>, AFB: Fn(A) -> >::Target, { match &self { From a8b72d04b0f072e68ebc8bbddb85047d3fd13dc9 Mon Sep 17 00:00:00 2001 From: Heman Gandhi Date: Sat, 11 Jan 2020 12:51:06 -0500 Subject: [PATCH 13/15] start of trying to use sequence --- fp-core/src/traverse.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/fp-core/src/traverse.rs b/fp-core/src/traverse.rs index 2dec7f5..532d620 100644 --- a/fp-core/src/traverse.rs +++ b/fp-core/src/traverse.rs @@ -7,15 +7,13 @@ use std::fmt::Debug; // Re-statement of the Haskell definition // (http://hackage.haskell.org/package/base-4.12.0.0/docs/Data-Traversable.html) -pub trait Traversable: Functor + Foldable -where - F: Applicative + Applicative<>::Target>, +pub trait Traversable: Functor + Foldable { // A is the source type, F the functor, and AFB a natural transformation // from A into F. Pursuant to Cats in Scala (https://www.scala-exercises.org/cats/traverse), // A would be a User, F a future, and Self would be a list. AFB would be the sort of "user // promise" function in the example. - fn traverse(&self, traverser: Box) -> HktInHkt + fn traverse(&self, traverser: Box) -> HktInHkt // (Self is a HKT so this works out) where AFB: Fn(>::Current) -> >::Target; From 8763fe57f9a7aa39c64456a824b2d72d6ecaaa15 Mon Sep 17 00:00:00 2001 From: Heman Gandhi Date: Sat, 11 Jan 2020 15:24:32 -0500 Subject: [PATCH 14/15] start sequence impl --- fp-core/src/traverse.rs | 39 ++++++++++++++------------------------- 1 file changed, 14 insertions(+), 25 deletions(-) diff --git a/fp-core/src/traverse.rs b/fp-core/src/traverse.rs index 532d620..bc939f9 100644 --- a/fp-core/src/traverse.rs +++ b/fp-core/src/traverse.rs @@ -7,40 +7,29 @@ use std::fmt::Debug; // Re-statement of the Haskell definition // (http://hackage.haskell.org/package/base-4.12.0.0/docs/Data-Traversable.html) -pub trait Traversable: Functor + Foldable -{ - // A is the source type, F the functor, and AFB a natural transformation - // from A into F. Pursuant to Cats in Scala (https://www.scala-exercises.org/cats/traverse), - // A would be a User, F a future, and Self would be a list. AFB would be the sort of "user - // promise" function in the example. - fn traverse(&self, traverser: Box) -> HktInHkt - // (Self is a HKT so this works out) - where - AFB: Fn(>::Current) -> >::Target; -} - -// Re-statement of the Haskell function -// (http://hackage.haskell.org/package/base-4.12.0.0/docs/Data-Traversable.html) -pub fn sequenceA(wrong_order: HktInHkt) -> HktInHkt +// that uses sequence as the basis for traverse, that is: +// class (Functor t, Foldable t) => Traverse t where +// sequence::(Applicative f) => t (f b) -> f (t b) +// +// Unfortunately, Rust doesn't handle a "for all" in the same way, so to Rust, +// we must know that T is functorial over F and just B and F is Applicative +// for B and T. +pub trait Traversable: + Functor + Foldable + Functor<>::Target> + Foldable<>::Target> where - T: Traversable + HKT<>::Target>, - F: Applicative + Applicative<>::Target>, - HktInHkt: Traversable + F: Applicative + Applicative<>::Target>, { - Traversable::::traverse(&wrong_order, Box::new(|x| x)) + fn sequence(tfb: HktInHkt) -> HktInHkt; } impl Traversable for Option where F: Applicative + Applicative>, { - fn traverse(&self, traverser: Box) -> HktInHkt - where - AFB: Fn(A) -> >::Target, - { - match &self { + fn sequence(tbf: HktInHkt) -> HktInHkt { + match tbf { Option::None => F::of(Option::None), - Option::Some(a) => F::of(Option::Some(traverser(*a))), + Option::Some(fb) => F::of } } } From 7706ff3a50a0a8f49289a437d88534c40198bcb4 Mon Sep 17 00:00:00 2001 From: Heman Gandhi Date: Fri, 24 Jan 2020 23:17:02 -0500 Subject: [PATCH 15/15] IDK even --- fp-core/src/traverse.rs | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/fp-core/src/traverse.rs b/fp-core/src/traverse.rs index bc939f9..9c30af7 100644 --- a/fp-core/src/traverse.rs +++ b/fp-core/src/traverse.rs @@ -1,7 +1,8 @@ -use crate::applicative::Applicative; +use crate::applicative::{Applicative, Applicator}; use crate::foldable::Foldable; use crate::functor::Functor; use crate::hkt::{HktInHkt, HKT}; +use crate::pure::Pure; use std::fmt::Debug; @@ -18,25 +19,27 @@ pub trait Traversable: Functor + Foldable + Functor<>::Target> + Foldable<>::Target> where F: Applicative + Applicative<>::Target>, + Self: + Functor + Foldable + Functor<>::Target> + Foldable<>::Target>, { fn sequence(tfb: HktInHkt) -> HktInHkt; } impl Traversable for Option where - F: Applicative + Applicative>, + F: Applicative + Applicative> + Pure>>, { fn sequence(tbf: HktInHkt) -> HktInHkt { match tbf { Option::None => F::of(Option::None), - Option::Some(fb) => F::of + Option::Some(fb) => fb.ap(F::of(Box::new(|v| Option::Some(v)))), } } } impl Traversable for Vec where - F: Applicative + Applicative>, + F: Applicative + Applicative> + Pure>>, { fn traverse(&self, traverser: Box) -> HktInHkt where @@ -50,19 +53,24 @@ where } return acc; } + + fn sequence(tbf: HktInHkt) -> HktInHkt { + let acc = F::of(vec![]); + for item in tbf { + acc = item.ap(F::of(Box::new(|b| acc.ap(F::of(Box::new(|v| v.extend(&[b]))))))) + } + return acc; + } } impl Traversable for Result where - F: Applicative + Applicative>, + F: Applicative + Applicative> + Pure>>, { - fn traverse(&self, traverser: Box) -> HktInHkt - where - AFB: Fn(A) -> >::Target, - { - match &self { + fn sequence(tbf: HktInHkt) -> HktInHkt { + match tbf { Result::Err(e) => F::of(Result::Err(e)), - Result::Ok(a) => F::of(Result::Ok(traverser(*a))), + Result::Ok(fb) => fb.ap(F::of(Box::new(|v| Result::Ok(v)))), } } }