12

I'm trying to implement an Agent that behaves as a dice :

defmodule Dice do
  @on_load :seed_generator

  def start_link(opts \\ []) do
    Agent.start_link(fn -> [] end, name: __MODULE__)
  end

  def roll(n, val) do
    Agent.cast(__MODULE__, fn(_) ->
      Stream.repeatedly(fn -> :random.uniform(val) end)
      |> Enum.take(n)
    end)
  end

  def seed_generator do
    :random.seed(:erlang.now)
    :ok
  end
end

However, the generated numbers are the same, every time I restart iex. What am I doing wrong ? Is the seed not working because the :random.uniform call is inside the Agent ? Or something related with the Stream maybe.

I GIVE TERRIBLE ADVICE
  • 9,578
  • 2
  • 32
  • 40
Kernael
  • 3,270
  • 4
  • 22
  • 42

1 Answers1

8

The seed_generator function is called in a different process than the one your Agent will be using. In fact that process doesn't even exist at the point this code is loaded. Try seeding the generator when starting the Agent:

defmodule Dice do
  def start_link(opts \\ []) do
    Agent.start_link(fn -> :random.seed(:erlang.now) end, name: __MODULE__)
  end

  def roll(n, val) do
    Agent.get(__MODULE__, fn(_) ->
      Stream.repeatedly(fn -> :random.uniform(val) end)
      |> Enum.take(n)
    end)
  end
end
Paweł Obrok
  • 22,568
  • 8
  • 74
  • 70
  • Seems to work, did this : `Agent.start_link(fn -> :random.seed(:erlang.now); [] end, name: __MODULE__)` to keep an empty list as the default state. Thank you. – Kernael May 25 '15 at 13:03
  • You're ignoring the state anyway in the `roll` function, so I don't think it matters. Your actual state is the process dictionary where the state of the random generator is kept. – Paweł Obrok May 25 '15 at 13:05
  • 2
    Instead of erlang.now(), the function os.timestamp() might be also good: http://www.erlang.org/doc/man/os.html#timestamp-0 Because erlang.now() might bring some problems. – h4cc May 25 '15 at 13:58