Skip to content

Instantly share code, notes, and snippets.

@avdi
Created January 9, 2015 20:32
Show Gist options
  • Select an option

  • Save avdi/d9d92640e570fcba2988 to your computer and use it in GitHub Desktop.

Select an option

Save avdi/d9d92640e570fcba2988 to your computer and use it in GitHub Desktop.
Behold, the null enumerator.
e = loop
e # => #<Enumerator: main:loop>
e.next # => nil
e.next # => nil
e.peek # => nil
e.size # => Infinity
e.rewind
e.next # => nil
@avdi
Copy link
Copy Markdown
Author

avdi commented Jan 9, 2015

The fact that this exists is a tribute to consistency. I feel like this may actually be a useful thing in certain generic programming scenarios, but for the life of me, in my current sleep-deprived state I can't think what those scenarios might look like. Any thoughts?

@avdi
Copy link
Copy Markdown
Author

avdi commented Jan 9, 2015

Like, I feel like this would make a good default argument in certain cases in order to avoid a special case.

@pnomolos
Copy link
Copy Markdown

pnomolos commented Jan 9, 2015

It's another way of writing while true do ... end, but whether or not that is useful is questionable at best.

@Peeja
Copy link
Copy Markdown

Peeja commented Jan 9, 2015

It's neat, but I'm not sure what I'd use it for. The other thing I'd call a null enumerator, [].each, I can come up with use cases for as null object. But the fact that loop is infinite and constant and gives you nil makes it seem pretty useless to me. I hope to be proven wrong, though. :)

@avdi
Copy link
Copy Markdown
Author

avdi commented Jan 9, 2015

It is not the same, because while does not give you an Enumerator.

@avdi
Copy link
Copy Markdown
Author

avdi commented Jan 9, 2015

Yeah, I'm just thinking that there may be certain scenarios where "always" makes sense instead of "never" as the default.

@AaronLasseigne
Copy link
Copy Markdown

Maybe there's some useful way to use it with zip.

> ('a'..'z').zip(loop).to_h
# => {"a"=>nil, "b"=>nil, "c"=>nil, "d"=>nil, "e"=>nil, "f"=>nil, "g"=>nil, "h"=>nil, "i"=>nil, "j"=>nil, "k"=>nil, "l"=>nil, "m"=>nil, "n"=>nil, "o"=>nil, "p"=>nil, "q"=>nil, "r"=>nil, "s"=>nil, "t"=>nil, "u"=>nil, "v"=>nil, "w"=>nil, "x"=>nil, "y"=>nil, "z"=>nil}

@Peeja
Copy link
Copy Markdown

Peeja commented Jan 9, 2015

It's certainly useful to have an infinite, constant enumerator, but I think the cases where you want it to be constantly nil are going to be rare.

Incidentally, what's the easiest way to make a constant enumerator, one that always returns, say 5? Off the top of my head, the best I've got is:

Enumerator.new do |y|
  loop do
    y.yield 5
  end
end

but that seems like way too much code for something so fundamental.

Actually, @avdi, loop breaks the consistency a bit because it doesn't yield anything to its block (that is, its block takes arity 0), and so its enumerator should return 0 things each time it's called. But since that doesn't quite make sense, it returns nil.

But if it did somehow return 0 things, this would be a much nicer way to create a constant enumerator:

loop.with_object(5)

Unfortunately, loop.with_object(5).first == [nil, 5].

@avdi
Copy link
Copy Markdown
Author

avdi commented Jan 9, 2015

Interestingly, @Peeja, at least next_values agrees with you:

e.next_values                   # => []

@AaronLasseigne
Copy link
Copy Markdown

@Peeja

[5].cycle

@avdi
Copy link
Copy Markdown
Author

avdi commented Jan 9, 2015

Nice.

@Peeja
Copy link
Copy Markdown

Peeja commented Jan 12, 2015

@AaronLasseigne Ooh, very nice.

@avdi That's interesting. It's a shame #with_object doesn't use use #next_values. I wonder if Ruby would accept a patch for it…

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment