As an example I picked a random function in the Stream
module
@spec chunk_every(Enumerable.t(), pos_integer) :: Enumerable.t()
def chunk_every(enum, count), do: chunk_every(enum, count, count, [])
Returns an Enumerable, which is technically true, but streams are different in many ways from the data we handle in Enum
, for example:
# Direcly related to the line in the Elixir source code I linked to
iex> Enum.chunk_every([1, 2, 3, 4, 5, 6], 2)
[[1, 2], [3, 4], [5, 6]]
iex> Stream.chunk_every([1, 2, 3, 4, 5, 6], 2)
#Stream<[
enum: [1, 2, 3, 4, 5, 6],
funs: [#Function<3.58486609/1 in Stream.chunk_while/4>]
]>
The fact that inspect
returns different results is a strong indicator to me that it is not the same.
Here is a more practice oriented example:
defmodule Printer do
def print([]), do: IO.puts("End of Input")
def print([something | rest]) do
IO.inspect(something)
print(rest)
end
end
iex> "asdf,11111,0000,last" |> String.split(",") |> Printer.print()
"asdf"
"11111"
"0000"
"last"
End of Input
:ok
iex> "asdf,11111,0000,last" |> String.splitter(",") |> Printer.print()
** (FunctionClauseError) no function clause matching in Printer.print/1
iex> "asdf,11111,0000,last" |> String.splitter(",") |> Enum.each(&IO.inspect/1)
"asdf"
"11111"
"0000"
"last"
:ok
Streams and Enumerables behave the same in many cases, and the Enum module knows how to treat streams, but they are (for practical purposes) not the same. Stream even has its own module to deal with streams, or convert enumerables into a stream, and vice versa Enum takes streams and converts them into enumerables.
Definition of enumerable
able to be counted by one-to-one correspondence with the set of all positive integers.
The definition of a stream is that it cannot be determinate, because as soon as it becomes determinate (as for example in reduce
), it stops being a stream - hence a stream cannot be counted, because as soon as you know a stream has x members, you converted it into an enumerable. Follows that a stream is not an enumerable.
Maybe I am missing something important here, please let me know if I do, but everything I know about streams and enumerables points to their functions should have returns with different types.
EDIT: I feel like I was just ranting - but what I actually wanted was either an explanation of why it’s not possible, or a prompt to open an issue on the Elixir source code.
EDIT2: I get that implementing protocols is different from the type now - I am still not sure why the typespec is Enumerable
though. Easier for the implementation?
EDIT3: Now I get it… duck typing. It still feels wrong to return Enumerable
for a (by definition) non enumerable.