After posting this in elixir (on irc & discord) I got a very decent reply that works out pretty fine, so I’m gonna close this topic for now with it!
First off, trying to generate %StreamData{}
myself, is kinda bad form, since it’s basically a private struct…
We looked for alternatives, and found StreamData.constant/0
. However, on its own, it always generates the same value.
Which makes a certain amount of sense when you think about how it’s called:
iex(1)> x = StreamData.constant(AuthToken.generate_key) # this is now evaluated, we're done here
%StreamData{generator: #Function<16.32994346/2 in StreamData.constant/1>}
iex(2)> x |> Enum.take(2)
[
ok: <<109, 179, 255, 84, 16, 160, 239, 132, 198, 157, 218, 254, 13, 160, 196, 137>>,
ok: <<109, 179, 255, 84, 16, 160, 239, 132, 198, 157, 218, 254, 13, 160, 196, 137>>
]
iex(3)>
The clue came from @michalmuskala:
You usually construct the opaque type one way or the other, you rarely construct it directly. For example the MapSet struct is opaque, but you can generate it by doing something like map(list(), &MapSet.new/1)
and the code we now have from @ericmj
defp gen_authtoken_key() do
StreamData.map(StreamData.list_of(StreamData.constant(:unused_tick)), fn _ ->
AuthToken.generate_key()
end)
end
what we’re doing here is using StreamData.constant/1
as a sort of clock generator, that drives the call of AuthToken.generate_key/0
… and it’s nice start, but we’re needlessly generating a list_of
which we then map
So let’s update this with all responses below!
defp gen_authtoken_key() do
StreamData.unshrinkable(
StreamData.bind(StreamData.constant(:unused), fn _ ->
StreamData.constant(AuthToken.generate_key())
end)
)
end
This is much cleaner! Since we use bind/2
, which is essentially made for this… better see what’s going on, we can use pipes to extract the flow:
defp gen_authtoken_key() do
gen_ticker = fn _ ->
StreamData.constant(AuthToken.generate_key())
end
StreamData.constant(:unused)
|> StreamData.bind(gen_ticker)
|> StreamData.unshrinkable()
end
We feed a stream of :unused
into gen_ticker
lambda, just so it will be constantly called. Since our call to AuthToken.generate_key/0
is wrapped in said lambda, it will be executed on every call, rather than on ge_ticker
’s declaration. Finally, we pass the so generated %StreamData{}
into unshrinkable/0
(because it is) before returning it.