Skip to content

Instantly share code, notes, and snippets.

@bryanhuntesl
Created April 21, 2020 08:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bryanhuntesl/0346597501d30434e4d78c8f189d5cc2 to your computer and use it in GitHub Desktop.
Save bryanhuntesl/0346597501d30434e4d78c8f189d5cc2 to your computer and use it in GitHub Desktop.

Creating a function that generates Streams in one line of code

What is a Stream

From the Elixir documentaion :

iex(8)> h Stream.resource

                  def resource(start_fun, next_fun, after_fun)

  @spec resource(
          (() -> acc()),
          (acc() -> {[element()], acc()} | {:halt, acc()}),
          (acc() -> term())
        ) :: Enumerable.t()

Emits a sequence of values for the given resource.

Similar to transform/3 but the initial accumulated value is computed lazily via
start_fun and executes an after_fun at the end of enumeration (both in cases of
success and failure).

Successive values are generated by calling next_fun with the previous
accumulator (the initial value being the result returned by start_fun) and it
must return a tuple containing a list of elements to be emitted and the next
accumulator. The enumeration finishes if it returns {:halt, acc}.

As the name says, this function is useful to stream values from resources.

## Examples

    Stream.resource(
      fn -> File.open!("sample") end,
      fn file ->
        case IO.read(file, :line) do
          data when is_binary(data) -> {[data], file}
          _ -> {:halt, file}
        end
      end,
      fn file -> File.close(file) end
    )

    iex> Stream.resource(
    ...>  fn ->
    ...>    {:ok, pid} = StringIO.open("string")
    ...>    pid
    ...>  end,
    ...>  fn pid ->
    ...>    case IO.getn(pid, "", 1) do
    ...>      :eof -> {:halt, pid}
    ...>      char -> {[char], pid}
    ...>    end
    ...>  end,
    ...>  fn pid -> StringIO.close(pid) end
    ...> ) |> Enum.to_list()
    ["s", "t", "r", "i", "n", "g"]

Lets try it in practice. This anonymous function creates a Stream.resource/3 which generates a stream of numbers counting down from whatever number you provide.

Function creation:

s = fn(count) -> Stream.resource(fn -> count end, fn(count) -> case count do count when count > 0 -> {[count],count - 1}; count -> {:halt,count} end end, fn _ -> :ok end)  end

Function usage:

s.(15) |> Enum.each(&(IO.puts(&1)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment