FreeAst v0.3.2 FreeAst View Source

Implements something similar to Free Monad.

Usage

FreeAst.p/1 macro returns a program that can be interpreted later. Expressions to interpret are specified with effect/2 and eval/1 calls. The first one is used to specify a desired effect, while the second one -- to evaluate another program.

Here we define two functions that return programs with effects:

defmodule FreeAstExample.Reader do
  @moduledoc false

  require FreeAst

  def read_line do
    FreeAst.p do
      effect IO, read_line()
    end
  end
end

defmodule FreeAstExample.Greater do
  @moduledoc false

  require FreeAst

  alias FreeAstExample.Reader

  def great do
    FreeAst.p do
      greating = effect Environment, greating()
      greating <> ", " <> eval Reader.read_line()
    end
  end
end

Now we can define interpreters for these:

defmodule FreeAstExample.IOInterpreter do
  @moduledoc false

  def interpret(IO, :read_line, []), do: "John"
end

defmodule FreeAstExample.EnvironmentInterpreter do
  @moduledoc false

  def interpret(Environment, :greating, []), do: "Hi"
end

For our first program we can use the first interpreter, but for the second one we need both, so we compose interpreters:

defmodule FreeAstExample.MainInterpreter do
  @moduledoc false

  alias FreeAst.Interpreter
  alias FreeAstExample.{IOInterpreter, EnvironmentInterpreter}

  def interpreter do
    Interpreter.noop()
    |> Interpreter.compose(IO, &IOInterpreter.interpret/3)
    |> Interpreter.compose(Environment, &EnvironmentInterpreter.interpret/3)
  end
end

So now we can execute it with FreeAst.interpret/2:

iex(1)> alias FreeAstExample.{Greater, MainInterpreter}
iex(2)> FreeAst.interpret(Greater.great(), MainInterpreter.interpreter())
"Hi, John"

Link to this section Summary

Types

Interpreter is a function that takes three arguments: action kind which is an atom or an alias, action name which is an atom, and a list of arguments of the action.

Represents a program (returned by p/1 macro).

Functions

Short syntax for defining functions that return programs

Interprets a program with the supplied interpreter.

Creates a program from the supplied code block.

Link to this section Types

Link to this type

interpreter()

View Source
interpreter() :: (atom(), atom(), [term()] -> term())

Interpreter is a function that takes three arguments: action kind which is an atom or an alias, action name which is an atom, and a list of arguments of the action.

Represents a program (returned by p/1 macro).

Link to this section Functions

Link to this macro

defast(func_signature, do_body)

View Source (macro)

Short syntax for defining functions that return programs

defmodule FreeAstExample.DefastExample do
  @moduledoc false

  import FreeAst, only: [defast: 2]

  require FreeAst

  defast program_v1 do
    :value
  end

  def program_v2 do
    FreeAst.p do
      :value
    end
  end
end


iex(1)> import FreeAstExample.DefastExample
iex(2)> import FreeAst.Interpreter, only: [noop: 0]
iex(3)> result_v1 = FreeAst.interpret(program_v1(), noop())
iex(4)> result_v2 = FreeAst.interpret(program_v2(), noop())
iex(5)> result_v1 == result_v2
true
Link to this function

interpret(program, interpreter)

View Source
interpret(program(), interpreter()) :: term()

Interprets a program with the supplied interpreter.

Link to this macro

p(ast_in_do_block)

View Source (macro)

Creates a program from the supplied code block.

iex(1)> program =
...(1)>  FreeAst.p do
...(1)>    x = effect IO, read_line()
...(1)>    "Hi, " <> x
...(1)>  end
iex(2)> FreeAst.interpret(program, fn IO, :read_line, [] -> "Kek" end)
"Hi, Kek"