README.md

# Streaming

`streaming` is a [stream](https://hexdocs.pm/elixir/Stream.html) comprehension macro for Elixir
inspired by [`for`](https://hexdocs.pm/elixir/Kernel.SpecialForms.html#for/1).

## `streaming` vs `for`

| Feature              | `for`                                | `streaming`            |
|----------------------|--------------------------------------|------------------------|
| Evaluation           | Eager                                | Lazy                   |
| Returns              | Enumerable (or anything if reducing) | Stream                 |
| Generators           | yes                                  | yes                    |
| Bitstring generators | yes                                  | yes                    |
| Filters              | yes                                  | yes                    |
| `uniq`               | yes                                  | yes                    |
| `into`               | collection returned                  | collect as side-effect |
| `reduce`             | yes                                  | n/a - just use `for`   |
| `scan`               | no                                   | yes                    |
| `unfold`             | n/a                                  | yes                    |
| `resource`           | n/a                                  | yes                    |
| `transform`          | n/a                                  | yes                    |


## Examples

Generate a stream of all permutations of x, y, and z.
```elixir
streaming x <- 1..10, y <- 11..20, z <- 21..30 do
  {x, y, z}
end
```

Just like `for`, mismatching values are rejected.
```elixir
users = [user: "john", admin: "meg", guest: "barbara"]

streaming {type, name} when type != :guest <- users do
  String.upcase(name)
end
|> Enum.to_list()

=> ["JOHN", "MEG"]
```

Filter for keeping only even numbers
```elixir
streaming x <- 1..100, rem(y, 2) == 0 do
  x
end
|> Enum.take(3)

=> [2, 4, 6]
```

`uniq` works like expected.
```elixir
streaming x <- ~c"ABBA", uniq: true do
  x + 32
end
|> Enum.to_list()

=> ~c"ab"
```

Infinite stream of multiples of 2 with `unfold`, based on
[`Stream.unfold/2`](https://hexdocs.pm/elixir/Stream.html#unfold/2).
```elixir
streaming unfold: 1 do
  n -> {n, n * 2}
end
|> Enum.take(10)

=> [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
```

Support for [`Stream.scan/3`](https://hexdocs.pm/elixir/Stream.html#scan/3) is included
(but not [`Stream.scan/2`](https://hexdocs.pm/elixir/Stream.html#scan/2))
```elixir
streaming x <- 1..5, scan: 0 do
  acc -> x + acc
end
|> Enum.to_list()

=> [1, 3, 6, 10, 15]
```

Stream values from a resource and close it when the stream ends. The `after` block is required
when using a resource.
```elixir
streaming resource: StringIO.open("string") |> elem(1) do
  pid ->
    case IO.getn(pid, "", 1) do
      :eof -> {:halt, pid}
      char -> {[char], pid}
    end
after
  pid -> StringIO.close(pid)
end
|> Enum.to_list()

=> ["s", "t", "r", "i", "n", "g"]
```

Transforming data from a resource with an enumerable. The `after` block is optional when
transforming.
```elixir
streaming i <- 1..100, transform: StringIO.open("string") |> elem(1) do
  pid ->
    case IO.getn(pid, "", 1) do
      :eof -> {:halt, pid}
      char -> {[{i, char}], pid}
    end
after
  pid -> StringIO.close(pid)
end
|> Enum.to_list()

=> [{1, "s"}, {2, "t"}, {3, "r"}, {4, "i"}, {5, "n"}, {6, "g"}]
```

Bitstring generators are supported.
```elixir
pixels = <<213, 45, 132, 64, 76, 32, 76, 0, 0, 234, 32, 15, 34>>

streaming <<r::8, g::8, b::8 <- pixels>> do
  {r, g, b}
end
|> Enum.to_list()

=> [{213, 45, 132}, {64, 76, 32}, {76, 0, 0}, {234, 32, 15}]
```

Bitstring generators can be combined with `scan` and `transform`!
```elixir
streaming <<i::8 <- "string">>, transform: StringIO.open("string") |> elem(1) do
  pid ->
    case IO.getn(pid, "", 1) do
      :eof -> {:halt, pid}
      char -> {[{char, i}], pid}
    end
after
  pid -> StringIO.close(pid)
end
|> Enum.to_list()

=> [{"s", 115}, {"t", 116}, {"r", 114}, {"i", 105}, {"n", 110}, {"g", 103}]
```


Note that `into` uses [`Stream.into/2`](https://hexdocs.pm/elixir/Stream.html#into/3)
and streams values into a collectable _as a side-effect_.
```elixir
{:ok, io_device} = StringIO.open("")
io_stream = IO.stream(io_device, :line)

streaming s <- ["hello", "there"], into: io_stream do
  String.capitalize(s)
end
|> Enum.to_list()

=> ["Hello", "There"]

## Values has been streamed into io_stream as well
StringIO.contents(io_device)

=> {"", "HelloThere"}
```