aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md7
-rw-r--r--src/lib.rs25
2 files changed, 21 insertions, 11 deletions
diff --git a/README.md b/README.md
index fefe68d..a24e6a8 100644
--- a/README.md
+++ b/README.md
@@ -3,8 +3,8 @@ This is a proof-of-concept. Please do not use this for production code, unless y
There probably are tons of bugs. Pull requests are more than welcome.
# Description
-This is a simple (and possibly wrong) port of Haskell's [Control.Monad.Free](https://hackage.haskell.org/package/free/docs/Control-Monad-Free.html) package to Rust, using the traits from [higher](https://crates.io/crates/higher).
-This crate uses macros to generate a unique Free Monad type for each Functor.
+This is a simple port of Haskell's [Control.Monad.Free](https://hackage.haskell.org/package/free/docs/Control-Monad-Free.html) package to Rust, using the traits from [higher](https://crates.io/crates/higher).
+This crate uses macros to generate a unique Free Monad type for each user-supplied Functor.
# Usage
The usage is rather straightforward. First, you create your Functor type, then call the `free!` macro to create the actual Free Monad type based on it. For example:
@@ -148,5 +148,8 @@ fn main() {
}
```
+## An even more involved example
+The "examples/text-adventure" folder in the source repo contains a (very) short text-adventure to illustrate the usage of a Free Monads as an embedded Domain Specific Language. It also shows some potential show-stoppers one should be aware of when creating a Free Monad based eDSL.
+
# A note about the origin of this code
The work on this project started at stillalive studios. The original goal was to learn about potential applications of Free Monads in game development, but this project has meanwhile outgrown that original plan, and has become a full proof-of-concept implementation for Free Monads in Rust. \ No newline at end of file
diff --git a/src/lib.rs b/src/lib.rs
index 0b3abd2..e15d7cc 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -28,20 +28,25 @@
//! # How to use the macro?
//!
//! For details, please see the documentation of the [free] macro.
-//! In short, the syntax is either `free!(FreeMonadTypeName<'a, A>, FunctorItsBasedOn<FreeMonadTypeName<'a, A>>)`,
-//! or, if the lifetime of the Free Monad depends on the lifetime of the function passed to the Functor's fmap function,
-//! `free!(<'a>, FreeMonadTypeName<'a,A>, FunctorItsBasedOn<'a,FreeMonadTypeName<'a,A>>)`, where `'a` is the affected lifetime.
+//! In short, the syntax is either
+//! `free!(FreeMonadTypeName<'a,A>, FunctorItsBasedOn<FreeMonadTypeName<'a,A>>)`,
+//! or, if the lifetime of the Free Monad depends on the lifetime of the function passed to the Functor's fmap function,
+//! `free!(<'a>, FreeMonadTypeName<'a,A>, FunctorItsBasedOn<'a,FreeMonadTypeName<'a,A>>)`,
+//! where `'a` is the affected lifetime.
//!
//! # Examples
//! The project's repository contains a folder named "examples", which at the moment contains a tiny text adventure that shows how such a game
//! could be implemented with Free Monads. The example highlights both, features and (current) limitations of Free Monads in Rust.
//!
-//! In addition, there is the "tests" folder, which contains integration tests, that show the syntax of the `free!()` macro in action.
+//! In addition, there is the "tests" folder, which contains integration tests, that show more of the syntax of the `free!()` macro in action.
//!
//! # Why a Macro?
//! Until [non-lifetime binders](https://github.com/rust-lang/rust/issues/108185) become stable, this seems to be the easiest way.
-//! In generic code, the type signature would be `enum Free<A,F> where F : Functor<Free<A,F>>`. If one now wants to implement the [`Functor`][higher::Functor]
-//! trait for this, it is not really possible to express the `Target<T> = Free<A,F::Target<Free<A,F::Target<...>>>>` generic associated type.
+//! In generic code, the type signature would be
+//! `enum Free<A,F> where F : Functor<Free<A,F>>`.
+//! If one now wants to implement the [`Functor`][higher::Functor] trait for this, it is not really possible to express the
+//! `Target<T> = Free<A,F::Target<Free<A,F::Target<...>>>>`
+//! generic associated type.
//!
//! See the [blog post about this crate](https://www.grois.info/posts/2023-03/2023-03-11-adventures-with-free-monads-and-higher.xhtml)
//! for a more detailed explanation.
@@ -54,11 +59,13 @@
//! There is work ongoing to [add explicit clone support to higher](https://github.com/bodil/higher/issues/6) though, so this might no longer be an issue with
//! later higher versions.
+#[doc(hidden)] //that this is re-exported is an implementation detail. Users should import directly from higher imho.
pub extern crate higher;
/// The macro that generates a Free [`Monad`][higher::Monad] type for a given [`Functor`][higher::Functor].
///
-/// To declare a Free [`Monad`][higher::Monad] over a [`Functor`][higher::Functor] named `Funky<A>`, the syntax would be `free!(FreeFunky<A>, Funky<FreeFunky<A>>)`.
+/// To declare a Free [`Monad`][higher::Monad] over a [`Functor`][higher::Functor] named `Funky<A>`, the syntax would be
+/// `free!(FreeFunky<A>, Funky<FreeFunky<A>>)`.
/// This declares an enum named `FreeFunky<A>`, and implements all traits needed for it to be a [`Monad`][higher::Monad].
///
/// # Restrictions
@@ -169,8 +176,8 @@ pub extern crate higher;
/// `fn lift_f(functor : Option<A>) -> FreeOption<A>` and
/// `fn retract(self : FreeOption<A>) -> Option<A>`
///
-/// `lift_f()` converts a base Functor into the corresponding Free Monad, meaning that the Functor gets wrapped in `Free`, and the value it holds gets
-/// mapped into a `Pure`. The (simplified for readability) formula is:
+/// `lift_f()` converts a base Functor into the corresponding Free Monad, meaning that the Functor gets wrapped in `Free`, and the values it holds get
+/// mapped into `Pure`. The (simplified for readability) formula is:
/// `Self::Free(functor.fmap(|a| Self::Pure(a)))`
///
/// `retract()` is the left-inverse of `lift_f()`. `|x| retract(lift_f(x))` is (ignoring type coercion) equivalent to [`identity`][std::convert::identity]: