Object#each?

A weird idea just popped into my head:

class Object
  def each
    yield self
  end
end

Lets you enumerate without having to worry about whether or not you have a
collection. Thoughts? Hidden ramifications?

Ugh, did I just say collection? I thought my .Net days were behind me....I
meant, of course, an enumerable...

···

On Tue, Nov 30, 2010 at 12:44 PM, Andrew Wagner <wagner.andrew@gmail.com>wrote:

A weird idea just popped into my head:

class Object
  def each
    yield self
  end
end

Lets you enumerate without having to worry about whether or not you have a
collection. Thoughts? Hidden ramifications?

Consider code like this:

  def do_sth_on(this)
    if this.kind_of?(Enumerable)
      this.map {|item| do_sth_on(item) }
    else
      # whatever
    end
  end

This is a method that either works on a collection or on single item. As
soon as a you make Object behave like Enumerable, this breaks and leads
to recursion.
Always very dangerous to modify core classes and in this case it seems
suicidal :slight_smile:

-- niklas

···

On Wed, 2010-12-01 at 02:44 +0900, Andrew Wagner wrote:

A weird idea just popped into my head:

class Object
  def each
    yield self
  end
end

Lets you enumerate without having to worry about whether or not you have a
collection. Thoughts? Hidden ramifications?

A weird idea just popped into my head:

class Object
   def each
     yield self
   end
end

Lets you enumerate without having to worry about whether or not you have a
collection. Thoughts? Hidden ramifications?

First of all if you implement #each then you should also include Enumerable because the expectation would be that you also have all the nice methods like #select, #map etc. This makes all objects Enumerable which does not seem a good idea. One drawback of this approach is that you won't notice if you accidentally pass something to a method which should be Enumerable (I mean a real collection) - or at least the issue might take some time to be detected.

Ugh, did I just say collection? I thought my .Net days were behind me....I
meant, of course, an enumerable...

:slight_smile:

Cheers

  robert

···

On 11/30/2010 06:45 PM, Andrew Wagner wrote:

On Tue, Nov 30, 2010 at 12:44 PM, Andrew Wagner<wagner.andrew@gmail.com>wrote:

Well, sure. The whole point is to write, instead of the above:

def do_sth_on(this)
  this.each do
    #whatever
  end
end

···

On Wed, Dec 1, 2010 at 8:52 AM, niklas | brueckenschlaeger < niklas@brueckenschlaeger.de> wrote:

Consider code like this:

def do_sth_on(this)
   if this.kind_of?(Enumerable)
     this.map {|item| do_sth_on(item) }
   else
     # whatever
   end
end

This is a method that either works on a collection or on single item. As
soon as a you make Object behave like Enumerable, this breaks and leads
to recursion.

A weird idea just popped into my head:

class Object
  def each
    yield self
  end
end

Lets you enumerate without having to worry about whether or not you have
a
collection. Thoughts? Hidden ramifications?

First of all if you implement #each then you should also include Enumerable
because the expectation would be that you also have all the nice methods
like #select, #map etc. This makes all objects Enumerable which does not
seem a good idea.

An interesting point. I think I agree that cluttering Object with all the
Enumerable methods seems like a bad idea. I guess my thinking was that this
would be useful just for the purposes of having #each available, without
including Enumerable. This avoids the [*items] garbage.

One drawback of this approach is that you won't notice if you accidentally
pass something to a method which should be Enumerable (I mean a real
collection) - or at least the issue might take some time to be detected.

This is an interesting argument, because it sounds an awful lot like the
arguments I usually hear against duck-typing and dynamic typing in general.
Don't those also allow issues which "might take some time to be detected"
too? It seems like the answer is, as always, that's what specs/tests are
for.

One other thing that I thought of after my original email is that it might
be preferable to change it to "yield self unless nil?", because it could be
odd to have NilClass#each.

Thanks for the thoughts!

···

On Tue, Nov 30, 2010 at 4:40 PM, Robert Klemme <shortcutter@googlemail.com>wrote:

On 11/30/2010 06:45 PM, Andrew Wagner wrote:

On Tue, Nov 30, 2010 at 12:44 PM, Andrew Wagner<wagner.andrew@gmail.com >> >wrote:

I think he means that you remove the base case that recursive functions may
be relying on. Here is an example:

# a function, maybe in a lib you are using,
# silently doing its job, you don't even know its there
# to do its job it must recursively traverse enumerable objects
# in this example, it just prints them out
def print_nested(iteratable)
  iteratable.each do |*objects|
    if objects.size > 1
      print_nested(objects)
      next
    end
    object = objects.first
    if object.respond_to? :each
      print_nested object
    else
      puts object
    end
  end
end

# prints 1 through 16
print_nested [
  1,2,[3,4,[5]],
  { 6 => 7,
    8 => [ 9 , 10 , { 11 => 12..14 } ]
  },
  "15\n16",
]

# make everything respond to each
class Object
  def each
    yield self
  end
end

# stack overflow
print_nested [
  1,2,[3,4,[5]],
  { 6 => 7,
    8 => [ 9 , 10 , { 11 => 12..14 } ]
  },
  "15\n16",
]

···

On Wed, Dec 1, 2010 at 8:30 AM, Andrew Wagner <wagner.andrew@gmail.com>wrote:

> This is a method that either works on a collection or on single item. As
> soon as a you make Object behave like Enumerable, this breaks and leads
> to recursion.
>

Well, sure. The whole point is to write, instead of the above:

def do_sth_on(this)
  this.each do
   #whatever
end
end

A weird idea just popped into my head:

class Object
def each
yield self
end
end

Lets you enumerate without having to worry about whether or not you have
a
collection. Thoughts? Hidden ramifications?

First of all if you implement #each then you should also include Enumerable
because the expectation would be that you also have all the nice methods
like #select, #map etc. This makes all objects Enumerable which does not
seem a good idea.

An interesting point. I think I agree that cluttering Object with all the
Enumerable methods seems like a bad idea. I guess my thinking was that this
would be useful just for the purposes of having #each available, without
including Enumerable. This avoids the [*items] garbage.

I usually opt for making things explicit - at least if they are so
clutterless as "[*items]". :slight_smile:

One drawback of this approach is that you won't notice if you accidentally
pass something to a method which should be Enumerable (I mean a real
collection) - or at least the issue might take some time to be detected.

This is an interesting argument, because it sounds an awful lot like the
arguments I usually hear against duck-typing and dynamic typing in general.
Don't those also allow issues which "might take some time to be detected"
too? It seems like the answer is, as always, that's what specs/tests are
for.

Hm, not sure I agree. With duck typing you will immediately know if
an object does not implement a required method. Granted, you can pass
in all objects that satisfy the used contract without "noticing". But
then again that's the whole point of duck typing. :slight_smile: Maybe my
argument is not too important though. The main point would be anyway
the first argument against, that then everything is Enumerable and the
module Enumerable basically becomes meaningless.

One other thing that I thought of after my original email is that it might
be preferable to change it to "yield self unless nil?", because it could be
odd to have NilClass#each.

No. You would simply use inheritance and implement a special version
in class NilClass which throws, e.g.

class NilClass
  def each
    raise NoMethodError, "undefined method `each` for %p" % self
  end
end

Thanks for the thoughts!

You're welcome! It's an interesting exchange.

Kind regards

robert

···

On Wed, Dec 1, 2010 at 12:00 AM, Andrew Wagner <wagner.andrew@gmail.com> wrote:

On Tue, Nov 30, 2010 at 4:40 PM, Robert Klemme > <shortcutter@googlemail.com>wrote:

On 11/30/2010 06:45 PM, Andrew Wagner wrote:

On Tue, Nov 30, 2010 at 12:44 PM, Andrew Wagner<wagner.andrew@gmail.com >>> >wrote:

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/