diff --git a/.vscode/settings.json b/.vscode/settings.json index 4d9636b..ab2a30d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,14 @@ { - "rust-analyzer.showUnlinkedFileNotification": false + "rust-analyzer.showUnlinkedFileNotification": false, + "[rust]": { + "editor.rulers": [ + 100 + ] + }, + "[markdown]": { + "editor.rulers": [ + 99, + 100 + ] + } } \ No newline at end of file diff --git a/book/book.toml b/book/book.toml index 0acaaa0..caea406 100644 --- a/book/book.toml +++ b/book/book.toml @@ -6,6 +6,7 @@ src = "src" title = "Monads in Rust" [build] +build-dir = "book" create-missing = false [output.html] diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index 4ecbca9..0f6afcf 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -6,3 +6,7 @@ - [Implementation](./ch02/s00-implementation.md) - [Usage]() - [Current Implementation Concerns](./ch04/s00-concerns.md) + - [Alternative Monad Traits](./ch04/s01-alternatives.md) + - [Lifetimes](./ch04/s02-lifetimes.md) + - [Stackless](./ch04/s03-stackless.md) + - [Covariance](./ch04/s04-covariance.md) diff --git a/book/src/ch04/s00-concerns.md b/book/src/ch04/s00-concerns.md index 0fbe150..146cac8 100644 --- a/book/src/ch04/s00-concerns.md +++ b/book/src/ch04/s00-concerns.md @@ -1,32 +1,19 @@ # Concerns (questions) with the current implementaion +## There exist alternative `Functor` implementations + +See the [relevant subchapter](s01-alternatives.md) + +## It might be better to have a per-lifetime trait for `Functor`s + +See the [relevant subchapter](s02-lifetimes.md) + +## `Stackless` is kind of bad + +See the [relevant subchapter](s03-stackless.md) + +## `WeakFunctor::F<'a, A>` is not (yet) covariant over the lifetime `'a` + +See the [relevant subchapter](s04-covariance.md) + ## Can `WeakFunctor` be an associated type of a `Functor` instead of its supertype? - -## `Clone`-`FnMut` category functors - -## `Copy`-`Fn` category functors - -## `WeakFunctor::F<'a, A>` is not (yet) covariant over the lifetime `'a`. - -### Specific case: `Stackless<'a>` isn't covariant. -Current hypothesis is that this comes from `EvalTree<'a>` being invariant over `'a` -due to `FnOnce` being invariant over its output, -which in turn comes from present typesysten limitations. - -## It might be better to have a per-lifetime trait for `Functor`s. - -Current: -```rust -pub trait WeakFunctor { - type F<'a, A: 'a>: 'a - where - Self: 'a; -} -``` - -Proposed: -```rust -pub trait WeakFunctor<'a>: 'a { - type F: 'a; -} -``` diff --git a/book/src/ch04/s01-alternatives.md b/book/src/ch04/s01-alternatives.md new file mode 100644 index 0000000..735e65b --- /dev/null +++ b/book/src/ch04/s01-alternatives.md @@ -0,0 +1,97 @@ +# Atlernatives to `Functor` trait + +## `_`-`FnOnce` category functors (current) + +Copied for reference. All following examples are in the same `Functor`-`Applicative`-`Monad` format +without extra (sub)traits like `WeakFunctor`, `Pure`, `ApplicativeLA2`, etc. . + +```rust +trait Functor { + type F<'a, A: 'a>: 'a + where + Self: 'a; + + fn fmap<'a, A: 'a, B: 'a>( + f: impl 'a + FnOnce(A) -> B, fa: Self::F<'a, A>, + ) -> Self::F<'a, B> + where + Self: 'a; +} + +fn fmap<'a, T: 'a + Functor, A: 'a, B: 'a>( + f: impl 'a + FnOnce(A) -> B, +) -> impl FnOnce(T::F<'a, A>) -> T::F<'a, B> { + |fa| T::fmap(f, fa) +} +``` + +## `Clone`-`Fn` category functors + +This is probably the closest representation to what Haskell views as a category of its types. + +```rust +trait Functor: Clone { + type F<'a, A: 'a + Clone>: 'a + Clone + where + Self: 'a; + + fn fmap<'a, A: 'a + Clone, B: 'a + Clone>( + f: impl 'a + Clone + Fn(A) -> B, fa: Self::F<'a, A>, + ) -> Self::F<'a, B> + where + Self: 'a; +} + +fn fmap<'a, T: 'a + Functor, A: 'a + Clone, B: 'a + Clone>( + f: impl 'a + Clone + Fn(A) -> B, +) -> impl 'a + Clone + Fn(T::F<'a, A>) -> T::F<'a, B> { + move |fa| T::fmap(f.clone(), fa) +} +``` + +## `Clone`-`FnMut` category functors + +We view use of `FnMut` for category's morphisms as somewhat controversial[^e]. +* Use of direct/indirect mutable references is, arguably, counter-functional[^e]. +* `Clone+FnMut` is, generally, nonsensical[^e]. +* Due to that, morphisms category isn't a subcategory, so can't be wrapped. +* Not being to wrap morphisms makes implementation of one specific `Applicative` method, +sequential application (`<*>` in Haskell, `seq` in RADN) *difficult*. + +```rust +trait Functor: Clone { + type F<'a, A: 'a + Clone>: 'a + Clone + where + Self: 'a; + + fn fmap<'a, A: 'a + Clone, B: 'a + Clone>( + f: impl 'a + FnMut(A) -> B, fa: Self::F<'a, A>, + ) -> Self::F<'a, B> + where + Self: 'a; +} +``` + +[^e]: elaborate? + +## `Copy`-`Fn` category functors + +```rust +trait Functor: Copy { + type F<'a, A: 'a + Copy>: 'a + Copy + where + Self: 'a; + + fn fmap<'a, A: 'a + Copy, B: 'a + Copy>( + f: impl 'a + Copy + Fn(A) -> B, fa: Self::F<'a, A>, + ) -> Self::F<'a, B> + where + Self: 'a; +} + +fn fmap<'a, T: 'a + Functor, A: 'a + Copy, B: 'a + Copy>( + f: impl 'a + Copy + Fn(A) -> B, +) -> impl 'a + Copy + Fn(T::F<'a, A>) -> T::F<'a, B> { + move |fa| T::fmap(f, fa) +} +``` diff --git a/book/src/ch04/s02-lifetimes.md b/book/src/ch04/s02-lifetimes.md new file mode 100644 index 0000000..0ff255b --- /dev/null +++ b/book/src/ch04/s02-lifetimes.md @@ -0,0 +1,18 @@ +# Making lifetimes a parameter of a trait instead of that of the GAT + +Current: +```rust +pub trait WeakFunctor { + type F<'a, A: 'a>: 'a + where + Self: 'a; +} +``` + +Proposed: +```rust +pub trait WeakFunctor<'a>: 'a { + type F: 'a; +} +``` + diff --git a/book/src/ch04/s03-stackless.md b/book/src/ch04/s03-stackless.md new file mode 100644 index 0000000..ee7c427 --- /dev/null +++ b/book/src/ch04/s03-stackless.md @@ -0,0 +1,5 @@ +# `Stackless<'a>` isn't covariant + +Current hypothesis is that this comes from `EvalTree<'a>` being invariant over `'a` +due to `FnOnce` being invariant over its output, +which in turn comes from present typesysten limitations. diff --git a/book/src/ch04/s04-covariance.md b/book/src/ch04/s04-covariance.md new file mode 100644 index 0000000..0cb0c7a --- /dev/null +++ b/book/src/ch04/s04-covariance.md @@ -0,0 +1,5 @@ +## `CovariantFunctor` not (yet) included in `Monad` + +## Specific case: `Stackless<'a>` isn't covariant + +See the [relevant subchapter](s03-stackless.md)