Witchraft combine monads, or where is discussion about witchraft?

Hello everibady,

recently I started experimenting with algae, and I found out that in a lot of cases I need to combine multiple monads. Is there way of do it in this library? Quick search show that in haskell they use something called monad transformers. Is it on the read map, or is there some witchcraft specific trick? Or how would you do it without transformers?

I’m particulary interested in merging State and Writer monads.

Thank you

2 Likes

Which library are you using State and Writer monads from? Those aren’t in algae or witchcraft.

Yes they are:

1 Like

Ahh, I’ve not used those before! I don’t think they were mentioned in the readme then. ^.^;

How are you wanting to combine them, I’d imagine if you just want them in scope at the same time then I’d initially try this to see if it would work?

my_writer
|> monad do
  my_state
  |> monad do
    # Use both?
  end
end

If that doesn’t work (maybe witchcraft’s monad isn’t recursively scoped safe?) then there’s probably a tuple combiner for them, hmm…

To my knowledge, currently there unfortunately isn’t a monad transformer stack implementation for Witchcraft, so you probably have to define them yourself.

An alternative might be to look into ‘effect systems’ and model effectful computations using that instead. (c.f. the Haskell library fused-effects)

1 Like

Thank you, for reference, I will check it out

I tried something like this:

monad %Algae.Writer{} do
   events <-
      monad %Algae.State{} do
         events <- get(&create/1)
         modify & mutate(&1, events)
         return events
       end
      tell(events)
   events <-
      monad %Algae.State{} do
         events <- get(&increment/1)
         modify & mutate(&1, events)
         return events
      end
  tell(events)
end
|>...

But I’m not sure how to run this :smiley: (sorry If I miss something obvious, I’m just learning those concepts)

1 Like

It doesn’t run just outright? Do you have a mini project on github or so that reproduces the issue along with some pseudo-code of what you are trying to accomplish? I’m curious to try. ^.^

Well, I don’t have project, I’m experimenting in one file :D, It basicali is mini cqrs system where you have domain functions create, increment, decrement, add that generate events that they should by applied on state using function mutate and I’m triing to create command handler for it using monads (using state to store actual state and after every domain function mutate it and also writer to store all events that been generated during execution, function test() is returning array of solutions, I managed to solve it without writer using chain (function exec2):

defmodule Witch do
  use Witchcraft.Monad
  use Witchcraft.Chain

  import Algae.State
  import Algae.Writer

  def create(agg) do
    if is_nil(agg) do
      [:created]
    end
  end

  def increment(agg) do
    if agg < 10 do
      [{:added, 1}]
    end
  end

  def decrement(agg) do
    if agg > 0 do
      [{:subsctracted, 1}]
    end
  end

  def add(agg, count) do
    if agg < 10 do
      [{:added, count}]
    end
  end

  def subsctract(agg, count) do
    if agg > 0 do
      [{:subsctracted, count}]
    end
  end

  def mutate(_agg, :created) do
    0
  end

  def mutate(agg, {:added, count}) do
    agg + count
  end

  def mutate(agg, {:subsctracted, count}) do
    agg - count
  end

  def mutate(agg, events) when is_list(events) do
    Enum.reduce(events, agg, &mutate(&2, &1))
  end

  def test() do
    [
      put(1) |> run(0),
      modify(& &1 + 1) |> run(0),
      get() |> run(0),
      get(& create/1) |> run(nil),
      monad %Algae.State{} do
        events <- get(& create/1)
        modify & mutate(&1, events)
        nevents <- get(& increment/1)
        modify & mutate(&1, nevents)
        return events ++ nevents
      end
      |> evaluate(nil),
      monad %Algae.State{} do
        events1 <- exec(& create/1)
        events2 <- exec(& increment/1)
        events3 <- exec(& increment/1)

        return events1 ++ events2 ++ events3
      end
      |> run(nil),
      exec1(& create/1) >>> exec2(& increment/1) >>> exec2(& increment/1) |> evaluate(nil),
      Witchcraft.Chain.chain do
        []
        exec2(& create/1)
        exec2(& increment/1)
        exec2(& increment/1)
        count <- [1, 2, 3]
        exec2(& add(&1, count))
      end,
      monad %Algae.State{} do
        exec(& create/1)
        exec(& increment/1)
      end |> evaluate(nil),
      monad %Algae.Writer{} do
        events <-
          monad %Algae.State{} do
            events <- get(&create/1)
            modify & mutate(&1, events)
            return events
          end
        tell(events)
        events <-
          monad %Algae.State{} do
            events <- get(&increment/1)
            modify & mutate(&1, events)
            return events
          end
        tell(events)
      end
      |> Algae.Writer.run(),
      tell("test"),
      new(1, "test") |> listen()
    ]
  end

  def exec(callback) do
    monad %Algae.State{} do
      events <- get(callback)
      modify & mutate(&1, events)
      return events
    end
  end

  def exec1(callback) do
    exec2(callback).([])
  end

  def exec2(callback) do
    fn old ->
      monad %Algae.State{} do
        events <- get(callback)
        modify & mutate(&1, events)
        return old ++ events
      end
    end
  end
end
1 Like

I wonder if @expede exists to be able to ask, hmm I guess not. Maybe it’s worth raising this combination issue on the witchcraft issue tracker? It would be a useful thing to add. :slight_smile:

3 Likes