Skip to content

Instantly share code, notes, and snippets.

@avdi
Created January 9, 2015 20:32
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save avdi/d9d92640e570fcba2988 to your computer and use it in GitHub Desktop.
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
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
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

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

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
Author

avdi commented Jan 9, 2015

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

@avdi
Copy link
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

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

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
Author

avdi commented Jan 9, 2015

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

e.next_values                   # => []

@AaronLasseigne
Copy link

@Peeja

[5].cycle

@avdi
Copy link
Author

avdi commented Jan 9, 2015

Nice.

@Peeja
Copy link

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