Ninigi

Ninigi

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.

Showing Posts 1 to 10

lud

lud

That function does not work with streams but does neither work for all enumerable types. It only accepts lists.

For instance, maps are enumerables:

iex(4)> Enum.chunk_every(%{a: 1, b: 2, c: 3}, 2)  
[[a: 1, b: 2], [c: 3]]

If your Printer module should work with any enumerable, it should use Enum.map/2 or Enum.each/2 instead of pattern matching on lists recursively. And that would work with streams too.

Ninigi

Ninigi OP

Thanks, that actually cleared up a lot for me!

Streams implement the enumerable protocol, but they are not enumerable.

iex>  i %{a: "a"}
Term
  %{a: "a"}
Data type
  Map
Reference modules
  Map
Implemented protocols

iex> i Stream.map(%{a: "a"}, & &1)
Term
  #Stream<[enum: %{a: "a"}, funs: [#Function<47.58486609/1 in Stream.map/2>]]>
Data type
  Stream
Description
  This is a struct. Structs are maps with a __struct__ key.
Reference modules
  Stream, Map
Implemented protocols
  Enumerable, IEx.Info, Inspect

I was pretty sure I must be missing something - but I couldn’t figure it out. Again, thanks :slight_smile:

NobbZ

NobbZ

How are they not?

Ninigi

Ninigi OP

Because by definition, they are literally not enumerable. You cannot count them, unless you convert them into an enumerable.

They are more like… I can’t think of a good word here… I keep thinking about “traversable”, but that’s not right, because that would indicate you can have trees, or nodes etc., right?

NobbZ

NobbZ

They are countable.

There are enough natural numbers to give each element in a stream an index.

Ninigi

Ninigi OP

They are not countable until they stop being a stream.

I don’t want to sound condescending, but here is an analogy:

enumerable: I give you a basket full of apples. I ask you to count them. There are 10 apples. Countable.

stream: I give you one apple. And another one, and another one, and another one. I ask you how many I am going to give you. You have no idea until I tell you “that’s it”. Uncountable.

gregvaughn

gregvaughn

Here’s my analogy. That is “counted”. Countable means that it is possible to count, not that it has already happened in the past.

Ninigi

Ninigi OP

Not sure if you are contradicting, or backing me :slight_smile:

NobbZ

NobbZ

Even the latter example is countable, as for each apple you increase the current count by one.

Being countable does not mean “finite”.

The set of natural numbers is countable.

The set of integers is countable, as there is a technique to map one natural number to each integer. 0 is zero, positive integers are n * 2, negative integers are mapped to 1 - (n * 2). Or something like that, I don’t remember exactly.

The set of rationals isn’t, as you can’t clearly map a single natural number to a single rational number.

Math differs between a finite countable, infinite countable and infinite uncountable set.

NobbZ

NobbZ

Contradicting.

Where Next? Top

Trending in Discussions Top

AstonJ
As the title says, please share what you’ve been up to with Elixir. Whether that’s been learning it, looking into it, making stuff with i...
2977 94592 917
New
cblavier
Hey there, It’s been more than a year since we started using LiveView as our main UI library and building a whole library of UI componen...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
heathen
Quite interesting article Google brought me. Didn’t find any mentions about it here. What do you think in general? Would you use togethe...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New
AstonJ
Since we have deprecated our Erlang sections (as we have dedicated Erlang Forums now) let’s add this thread for those who’d like to post ...
New
Null-logic-0
What IDE or editor are you using for Elixir development? Personally, I use Zed, and I really like it, but sometimes I wish there were a ...
New

Other Trending Topics Top

JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
webofbits
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself. My main conc...
#ai
New

Latest on Elixir Forum

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews