#FreeMonad

Lionhairdinolionhairdino
2024-12-18

" Free모나드 는 공짜라서 Free 모나드가 아닙니다. "
lionhairdino.github.io/posts/2
하스켈 을 공부하고 있고, 모나드 를 알고 있는 분들 중, Free 모나드가 손에 잘 잡히지 않는 분들이 볼만한 상상 글입니다.(쉽게 쓰려고 수학을 뺀 게 아니라, 몰라서 수학적인 건 없습니다.)(SNS를 시작한지 얼마 안되어, 여기 저기 모두 올려 봅니다. 중복 정보를 받으시는 분들은 이해 부탁 드립니다.)

2024-09-25

To my surprise, the idea I had last night is actually working just fine in #RustLang.

So, my #VisualNovel #SpareTimeProject is now probably going to use an #eDSL encoded as a #FreeMonad which is wrapped in a #StateMonadTransformer.

Now I just need to continue working on it. I haven't made much progress in the last 9 months...

Screenshot of the beginnings of an eDSL for a visual novel, implemented in Rust.

It currently only has 3 commands, which are changing the background, giving the player options, and presenting exposition.

The source code visible in the image is:
#[derive(Clone)]
pub enum Vn<'next, Next, TextureId>{
    Exposition{
        text : String,
        next : Next,
    },
    PlayerChoice{
        text : String,
        options : PlayerOptions<'next, Next>
    },
    SetBackground{
        background : TextureId,
        next : Next,
    }
}

impl<'a, A : 'a, TextureId> Functor<'a, A> for Vn<'a,A, TextureId>{
    type Target<T> = Vn<'a, T, TextureId>;

    fn fmap<B, F>(self, f: F) -> Self::Target<B>
    where
        F: Fn(A) -> B + 'a {
        match self {
            Vn::Exposition { text, next } => Vn::Exposition { text: text, next: f(next) },
            Vn::PlayerChoice { text, options } => Vn::PlayerChoice { text, options : options.fmap(f) },
            Vn::SetBackground { background, next } => Vn::SetBackground { background: background, next: f(next) },
        }
    }
}

free!(<'a>, pub FreeVnRaw<'a, A, TextureId>, Vn<'a, FreeVnRaw<'a, A, TextureId>, TextureId>);

pub type FreeVn<'a, A, TextureId, GameState> = StateT<'a, GameState, FreeVnRaw<'a, (A, GameState), TextureId>>;

pub fn exposition<'a, TextureId : Clone + 'a, GameState : Clone +'a>(text : String) -> FreeVn<'a, (), TextureId, GameState>{
    FreeVn::lift(FreeVnRaw::lift_f(Vn::Exposition { text, next: () }))
}
2024-05-17

Nicolas Rinaudo THE DEBATABLY FREE MONAD Scalar Conference 2024
youtube.com/watch?v=Yci07bMTcsM

2023-11-15

Yesterday I ported Haskell's State Monad Transformer to #RustLang. That should make tracking the game state in my #FreeMonad based embedded Domain Specific Language for scripting the story in my spare time #VisualNovel #GameDev project way easier.

Also, the currently released version of "higher" has the limitation that bindings in do-notation only work well with Copy types. The State Monad Transformer doesn't have this limitation and can store any state that is Clone.

2023-06-30

@joenash The blog posts haskellforall.com/2012/07/puri & haskellforall.com/2012/06/you- by @GabriellaG439 about #FreeMonad|s seem related. They don't contain this claim verbatim, but a kinda lemma of it: «[…] we can always factor out impure parts from any code by using free monads. Free monads let you decompose any impure program into a pure representation of its behavior and a minimal impure interpreter[…]»

@donsbot

2023-04-11

I've started some architecture work for my potential #VisualNovel spare time project. I must say I'm torn between using a #FreeMonad for storytelling, or going the usual #RustLang way of using a trait...

A Free Monad would decouple the storytelling from the user interaction completely, and its internal state would become trivial to manage.

A trait would allow to utilize Rust's borrow checker to ensure a situation's consistency. For instance one could tie a character's lifetime to the location.

2023-04-03

On the topic of #FreeMonad in #RustLang: I found out that there is another package on crates.io, that was released a few weeks before my higher-free-macro macro:
crates.io/crates/algar

Unlike my code algar is using a generic type instead of a macro, what is possible because it has different Functor/Monad/... traits that use an associated type to specify the domain.

On the other hand, algar has limitations that higher-free-macro does not have. Its Monad can only bind with FnOnce, for instance.

2023-04-02

I went ahead and published my small #FreeMonad crate for #RustLang officially today.

crates.io/crates/higher-free-m

This crate employs a macro to create a Free Monad type for a user-supplied Functor. The Free Monad is implemented as a recursive data type with Box as indirection.

It uses the traits from the "higher" crate by @bodil to express what a Functor/Monad/etc. is.

2023-04-02

Today I've added an example to my #FreeMonad in #RustLang project.
It is pretty bad code, but that's what happens if one intentionally makes bad decisions in order to highlight issues and potential workarounds...

For instance, I used a `Vec` where a 3-element array would have sufficed, making me write a ton more `clone()` calls than I ever wanted...

Still, I'm pretty happy how small the interpreter turned out, and how easy it was to focus on the actual game logic:
github.com/soulsource/higher-f

Client Info

Server: https://mastodon.social
Version: 2025.04
Repository: https://github.com/cyevgeniy/lmst