README.md

# GenLoop

This library is an adaptation of awesome Ulf Wiger's library `:plain_fsm` for
Elixir. It reuses as `:plain_fsm` code as possible, but adds some features :

- OTP system behaviours for starting processes, stopping processes and name
  registering. That means that you can use the classic naming conventions as in
  `GenServer`:
  ```elixir
  name = atom_key
  name = {:global, key}
  name = {:via, Registry, {AppRegistry, key}}
  GenLoop.start_link(module, args, name: name)
  GenLoop.send(name, message)
  GenLoop.stop(name)
  ```
- `receive/2` macro inspired from
  [ashneyderman/plain_fsm_ex](https://github.com/ashneyderman/plain_fsm_ex) that
  automatically handles system messages. It handles parent `:EXIT` messages if
  your process traps exits too.

This is still a work in progress, notably the documentation must be completed.

## Installation

The package can be installed by adding `gen_loop` and `plain_fsm` to your list
of dependencies in `mix.exs`: We need to add `plain_fsm` because it is old on
Hex repository, we use a more up-to-date version of the library (mainly to
handle `terminate/2` callback).

```elixir
def deps do
  [
    {:gen_loop, "~> 0.1.0"},
    {:plain_fsm, github: "uwiger/plain_fsm", commit: "ae9eca8a8df8f61a32185b06882a55d60e62e904"},
  ]
end
```


## Why ?

This library is a direct concurrent to GenServer and GenFsm : it provides
selective receive and more freedom but makes it easier to shoot yourself in the
foot.

More info in [`plain_fsm` rationale](https://github.com/uwiger/plain_fsm/blob/master/doc/plain_fsm.md).

## How To ?

This section is still to be done, but basically :

- First, `use GenLoop, enter: :my_loop` in your module, where `:my_loop` is the
  name of a function in your module.
- Call `GenLoop.start_link(__MODULE__, args)`, you can also give options like
  `name`.
- Maybe `def init(args_list_from_start_link)` function that will return as in
  GenServer behaviour : `{:ok, state}`.
- Define your `my_loop(state_from_init)` function where your code now runs in a
  supervised process.
- Use the `receive/2` macro in your main state function (classic `receive/1` is
  fine in transient states):
  ```elixir
  def my_loop(state)
    my_state = change_stuf(state)
    receive my_state do   # Pass the state to use the macro
      rcall(from, msg) -> # Match a message from GenLoop.call/2
        reply(from, :ok)  # Reply with GenLoop.reply (automatically imported)
        my_loop(state)    # Don't forget to re-enter the loop
      rcast(msg) ->       # Match a message from GenLoop.cast
        state = do_stuff(msg)
        my_loop(state)
      msg ->              # Match a mere message from Kernel.send/2 or GenLoop.send/2
        state = do_stuff(msg)
        other_loop(state) # You can go to another loop to change state
    after
      1000 -> my_loop(state)
    end
    # You must not have any code after receive.
  end
  ```
- `receive` must be the last expression in the function.


Have a look at [loop_example.ex](https://github.com/niahoo/gen_loop/blob/master/lib/loop_example.ex).

## Alternative

GenLoop is designed for communicating processes : servers, FSMs, etc. Have a
look at the [Task](https://hexdocs.pm/elixir/Task.html) module if you just want
to supervise autonomous processes.

GenLoop is not a replacement for GenServer : if your have only one loop in your
module with a "catch all messages" clauses, you woud better use GenServer
instead of GenLoop.