Forum: Ruby-core [ruby-trunk - Bug #7248][Open] Shouldn't Enumerator::Lazy.new be private?

Posted by marcandre (Marc-Andre Lafortune) (Guest)
on 2012-10-31 05:23
(Received via mailing list)
Issue #7248 has been reported by marcandre (Marc-Andre Lafortune).

----------------------------------------
Bug #7248: Shouldn't Enumerator::Lazy.new be private?
https://bugs.ruby-lang.org/issues/7248

Author: marcandre (Marc-Andre Lafortune)
Status: Open
Priority: Normal
Assignee:
Category: core
Target version: 2.0.0
ruby -v: ruby 2.0.0dev (2012-10-29 trunk 37380) [x86_64-darwin10.8.0]


Is there a reason why Enumerator::Lazy.new is not private?

Lazy enumerators should be created with `Enumerable#lazy`. Moreover, 
there is no doc, and it can give unexpected results too.
Posted by mame (Yusuke Endoh) (Guest)
on 2012-11-05 15:02
(Received via mailing list)
Issue #7248 has been updated by mame (Yusuke Endoh).

Status changed from Open to Assigned
Assignee set to yhara (Yutaka HARA)

Yhara-san, I'd like your opinion about this ticket.

--
Yusuke Endoh <mame@tsg.ne.jp>
----------------------------------------
Bug #7248: Shouldn't Enumerator::Lazy.new be private?
https://bugs.ruby-lang.org/issues/7248#change-32427

Author: marcandre (Marc-Andre Lafortune)
Status: Assigned
Priority: Normal
Assignee: yhara (Yutaka HARA)
Category: core
Target version: 2.0.0
ruby -v: ruby 2.0.0dev (2012-10-29 trunk 37380) [x86_64-darwin10.8.0]


Is there a reason why Enumerator::Lazy.new is not private?

Lazy enumerators should be created with `Enumerable#lazy`. Moreover, 
there is no doc, and it can give unexpected results too.
Posted by yhara (Yutaka HARA) (Guest)
on 2012-12-07 17:17
(Received via mailing list)
Issue #7248 has been updated by yhara (Yutaka HARA).


Hi,

Enumerator::Lazy.new will be needed
(1) when you want to overwrite behavior of a lazy method. eg:

---
class Enumerator::Lazy
  def zip(*args, &block)
    enums = args.map(&:lazy)
    Lazy.new(self){|yielder, val|
      ary = [val] + enums.map{|e| e.next}
      if block
        yielder << block.call(ary)  # make lazy.zip{} behave lazy 
(currently it doesn't because enum.zip{} is eager)
      else
        yielder << ary
      end
    }
  end
end

fizz = [nil, nil, nil, nil, "Fizz"].cycle
buzz = [nil, nil, "Buzz"].cycle
p fizz.lazy.zip(buzz){|f, b| "#{f}#{b}"}.first(20)
---

(2) when you want to add a new Enumerable method and its lazy version. 
eg:

---
module Enumerable
  def filter_map(&block)
    self.map(&block).compact
  end
end

class Enumerator::Lazy
  def filter_map(*args, &block)
    Lazy.new(self){|yielder, val|
      result = block.call(val)
      yielder << result if result
    }
  end
end

p [11,12,13].filter_map{|i| i*i if i.even?}  #=> [144]
p (1..Float::INFINITY).lazy.filter_map{|i| i*i if i.even?}.first(20)
---
----------------------------------------
Bug #7248: Shouldn't Enumerator::Lazy.new be private?
https://bugs.ruby-lang.org/issues/7248#change-34511

Author: marcandre (Marc-Andre Lafortune)
Status: Assigned
Priority: Normal
Assignee: yhara (Yutaka HARA)
Category: core
Target version: 2.0.0
ruby -v: ruby 2.0.0dev (2012-10-29 trunk 37380) [x86_64-darwin10.8.0]


Is there a reason why Enumerator::Lazy.new is not private?

Lazy enumerators should be created with `Enumerable#lazy`. Moreover, 
there is no doc, and it can give unexpected results too.
Posted by marcandre (Marc-Andre Lafortune) (Guest)
on 2012-12-09 02:45
(Received via mailing list)
Issue #7248 has been updated by marcandre (Marc-Andre Lafortune).


Oh, interesting.

I'll do my best to document it, then.

This leads to more questions, though:

1) Is there a use case for the form without a block?

It's used internally (before calling lazy_set_method), but other than 
that I can't see a good use.

2) Is there a use case for specifying a symbol and arguments?

Again, internally we call lazy_set_method, to the symbol and arguments 
are only used by inspect, right?

3) Is there a good way to improve the `inspect` of such a lazy enum?

    p [11,12,13].filter_map{|i| i*i if i.even?} # => #<Enumerator::Lazy: 
#<Enumerator::Lazy: [1, 2, 3]>:each>

Notice the `each` and no appearance of `filter_map`

Doing `Lazy.new(self, :filter_map)` does not work and seems redundant.

Thanks

BTW, ultimately, I'm trying to see if Lazy.new can be adapted to accept 
a `size` lambda argument...

----------------------------------------
Bug #7248: Shouldn't Enumerator::Lazy.new be private?
https://bugs.ruby-lang.org/issues/7248#change-34548

Author: marcandre (Marc-Andre Lafortune)
Status: Assigned
Priority: Normal
Assignee: yhara (Yutaka HARA)
Category: core
Target version: 2.0.0
ruby -v: ruby 2.0.0dev (2012-10-29 trunk 37380) [x86_64-darwin10.8.0]


Is there a reason why Enumerator::Lazy.new is not private?

Lazy enumerators should be created with `Enumerable#lazy`. Moreover, 
there is no doc, and it can give unexpected results too.
Posted by yhara (Yutaka HARA) (Guest)
on 2013-01-11 20:59
(Received via mailing list)
Issue #7248 has been updated by yhara (Yutaka HARA).

Assignee changed from yhara (Yutaka HARA) to marcandre (Marc-Andre 
Lafortune)

marcandre (Marc-Andre Lafortune) wrote:
> Oh, interesting.
>
> I'll do my best to document it, then.
>
Thanks!

> This leads to more questions, though:
>
> 1) Is there a use case for the form without a block?
>
> It's used internally (before calling lazy_set_method), but other than that I 
can't see a good use.
>

That form is only for internal use.
You can remove the form without a block by replacing `Lazy.new(enum)'
with `Lazy.new(enum){|y, v| y<<v}'.

> 2) Is there a use case for specifying a symbol and arguments?
>

Actually I did not know lazy_initialize can take a symbol :-p
So I'm not sure about how the symbol and arguments are used,
but it looks like for internal use.
According to svn annotate, it is introduced to implement lazy.cycle 
(r35028).

> Again, internally we call lazy_set_method, to the symbol and arguments are only 
used by inspect, right?
>

That seems right.

> 3) Is there a good way to improve the `inspect` of such a lazy enum?
>
>     p [11,12,13].filter_map{|i| i*i if i.even?} # => #<Enumerator::Lazy: 
#<Enumerator::Lazy: [1, 2, 3]>:each>
>
> Notice the `each` and no appearance of `filter_map`
>
> Doing `Lazy.new(self, :filter_map)` does not work and seems redundant.
>
>
> BTW, ultimately, I'm trying to see if Lazy.new can be adapted to accept a `size` 
lambda argument...

Well, I have no idea. It would be difficult to design Lazy.new which may 
take
obj, block, symbol, args and size/size_fn...

BTW, I have a question. Document of to_enum says "see Enumerator#size=" 
but there is no such method. Is it a typo?
https://github.com/ruby/ruby/blob/e90ccd3beb0b9bf1...

----------------------------------------
Bug #7248: Shouldn't Enumerator::Lazy.new be private?
https://bugs.ruby-lang.org/issues/7248#change-35364

Author: marcandre (Marc-Andre Lafortune)
Status: Assigned
Priority: Normal
Assignee: marcandre (Marc-Andre Lafortune)
Category: core
Target version: 2.0.0
ruby -v: ruby 2.0.0dev (2012-10-29 trunk 37380) [x86_64-darwin10.8.0]


Is there a reason why Enumerator::Lazy.new is not private?

Lazy enumerators should be created with `Enumerable#lazy`. Moreover, 
there is no doc, and it can give unexpected results too.
Posted by marcandre (Marc-Andre Lafortune) (Guest)
on 2013-01-14 05:59
(Received via mailing list)
Issue #7248 has been updated by marcandre (Marc-Andre Lafortune).

Priority changed from Normal to High

Thanks for the answers.

So, the public API of Lazy.new has the following issues:

* not documented
* should require a block but doesn't
* accepts a method name & arguments which aren't really usable
* has a misleading "inspect"

Moreover, the only way for the user to create a lazy enumerator with a 
size is to subclass Lazy.

Here's what I propose as the official Lazy.new documentation and API:

    Lazy.new(obj, size=nil) { |yielder, *values| ... }

    Creates a new Lazy enumerator. When the enumerator is actually 
enumerated
    (e.g. by calling #force), +obj+ will be enumerated and each value 
passed
    to the given block. The block can yield values back using +yielder+.
    For example, to create a method +filter_map+ in both lazy and
    non-lazy fashions:

        module Enumerable
          def filter_map(&block)
            map(&block).compact
          end
        end

        class Enumerator::Lazy
          def filter_map
            Lazy.new(self) do |yielder, *values|
              result = yield *values
              yielder << result if result
            end
          end
        end

        (1..Float::INFINITY).lazy.filter_map{|i| i*i if 
i.even?}.first(5)
            # => [4, 16, 36, 64, 100]

Does this seem acceptable to you?

We should also change the result of the 'inspect' method for these user 
created lazy enumerators to remove the 'each', i.e:

    (1..Float::INFINITY).lazy.filter_map{|i| i*i if i.even?}.inspect
    # => #<Enumerator::Lazy: #<Enumerator::Lazy: 1..Infinity>>

If we want to provide an easy way to provide more info in the inspect, 
we could add extra parameters, but they shouldn't be used when 
iterating, only for inspection...

> BTW, I have a question. Document of to_enum says "see Enumerator#size=" but 
there is no such method. Is it a typo?

Typo fixed, thanks.


----------------------------------------
Bug #7248: Shouldn't Enumerator::Lazy.new be private?
https://bugs.ruby-lang.org/issues/7248#change-35397

Author: marcandre (Marc-Andre Lafortune)
Status: Assigned
Priority: High
Assignee: marcandre (Marc-Andre Lafortune)
Category: core
Target version: 2.0.0
ruby -v: ruby 2.0.0dev (2012-10-29 trunk 37380) [x86_64-darwin10.8.0]


Is there a reason why Enumerator::Lazy.new is not private?

Lazy enumerators should be created with `Enumerable#lazy`. Moreover, 
there is no doc, and it can give unexpected results too.
Posted by marcandre (Marc-Andre Lafortune) (Guest)
on 2013-01-14 06:00
(Received via mailing list)
Issue #7248 has been updated by marcandre (Marc-Andre Lafortune).

Assignee changed from marcandre (Marc-Andre Lafortune) to yhara (Yutaka 
HARA)


----------------------------------------
Bug #7248: Shouldn't Enumerator::Lazy.new be private?
https://bugs.ruby-lang.org/issues/7248#change-35398

Author: marcandre (Marc-Andre Lafortune)
Status: Assigned
Priority: High
Assignee: yhara (Yutaka HARA)
Category: core
Target version: 2.0.0
ruby -v: ruby 2.0.0dev (2012-10-29 trunk 37380) [x86_64-darwin10.8.0]


Is there a reason why Enumerator::Lazy.new is not private?

Lazy enumerators should be created with `Enumerable#lazy`. Moreover, 
there is no doc, and it can give unexpected results too.
Posted by yhara (Yutaka HARA) (Guest)
on 2013-01-16 16:11
(Received via mailing list)
Issue #7248 has been updated by yhara (Yutaka HARA).

Assignee changed from yhara (Yutaka HARA) to marcandre (Marc-Andre 
Lafortune)

marcandre (Marc-Andre Lafortune) wrote:
>         module Enumerable
>             end
>           end
>         end
>
>         (1..Float::INFINITY).lazy.filter_map{|i| i*i if i.even?}.first(5)
>             # => [4, 16, 36, 64, 100]
>
> Does this seem acceptable to you?
>
Yes!
----------------------------------------
Bug #7248: Shouldn't Enumerator::Lazy.new be private?
https://bugs.ruby-lang.org/issues/7248#change-35448

Author: marcandre (Marc-Andre Lafortune)
Status: Assigned
Priority: High
Assignee: marcandre (Marc-Andre Lafortune)
Category: core
Target version: 2.0.0
ruby -v: ruby 2.0.0dev (2012-10-29 trunk 37380) [x86_64-darwin10.8.0]


Is there a reason why Enumerator::Lazy.new is not private?

Lazy enumerators should be created with `Enumerable#lazy`. Moreover, 
there is no doc, and it can give unexpected results too.
Please log in before posting. Registration is free and takes only a minute.
Existing account (Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
No account? Register here.