witchcrafters / algae

Bootstrapped algebraic data types for Elixir
https://hex.pm/packages/algae
MIT License
342 stars 18 forks source link

Clarification on `Algae.Free` usage #72

Open florius0 opened 2 years ago

florius0 commented 2 years ago

Im not sure if issues is a correct place to request help, however I'm trying to understand Algae.Free and it isn't as easy as it might be

There is link to an article on free monads in docs

I tried to implement Haskell code in Elixir, and here's what I've got:

import Algae
import TypeClass
use Witchcraft

defmodule Toy do
  defsum do
    defdata Output do
      output :: any()
      next :: any()
    end

    defdata Bell do
      next :: any()
    end

    defdata(Done :: none())
  end

  defimpl TypeClass.Property.Generator, for: Toy.Output do
    def generate(_) do
      [1, 1.1, "", :a]
      |> Enum.random()
      |> Toy.Output.new()
    end
  end

  defimpl TypeClass.Property.Generator, for: Toy.Bell do
    def generate(_) do
      [1, 1.1, "", :a]
      |> Enum.random()
      |> Toy.Bell.new()
    end
  end

  defimpl TypeClass.Property.Generator, for: Toy.Done do
    def generate(_), do: Toy.Done.new()
  end

  definst Witchcraft.Functor, for: Toy.Output do
    def map(%{output: output, next: next}, fun), do: %Toy.Output{output: output, next: fun.(next)}
  end

  definst Witchcraft.Functor, for: Toy.Bell do
    def map(%{next: next}, fun), do: %Toy.Bell{next: fun.(next)}
  end

  definst Witchcraft.Functor, for: Toy.Done do
    def map(_, _), do: %Toy.Done{}
  end

  def of4(_, x) do
    Algae.Free.new(x ~> (&Algae.Free.Pure.new/1))
  end

  def output(x), do: fn y -> Algae.Free.new(Toy.Output.new(x, Algae.Free.Pure.new(y))) end

  def bell, do: fn y -> Algae.Free.new(Toy.Bell.new(Algae.Free.Pure.new(y))) end

  def done, do: Algae.Free.new(Toy.Done.new())

  def program do
    monad Algae.Free.new() do
      output(1)
      bell()
    end
  end
end

And here is the confusing moment Toy.program.(nil) returns %Algae.Free.Pure{pure: %Toy.Bell{next: %Algae.Free.Pure{pure: nil}}} instead of %Toy.Output{next: %Algae.Free.Pure{pure: %Toy.Bell{next: %Algae.Free.Pure{pure: nil}}}, output: 1}. It basically does not 'concatenate' previous value with the next one

Thanks in advance for your help!

github-actions[bot] commented 2 years ago

Thank you for submitting an issue! It means a lot that you took the time -- it helps us be better 🙏