"Readability" inflation

Hi --

To sum up: I'm totally with David and I think we should take his statement for what it is: a warning notice, a reminder to maybe more often try to keep some focus but not more - especially not any form of censorship.

It definitely wasn't a post about netiquette or trying to get people
to stop posting what they want. It was chiefly an analysis of why
there are so many of these suggestions, change requests, etc. that
either add punctuation or attempt to shorten already fairly concise
idioms. I thought others who have been puzzled by the phenomenon
might find it interesting.

David

···

On Sun, 30 Oct 2005, Robert Klemme wrote:

--
David A. Black
dblack@wobblini.net

Perhaps he means something like:

% cat sort_by.rb
module Enumerable
    alias _core_sort_by sort_by
    def sort_by(*args, &block)
        if args.empty?
            _core_sort_by(&block)
        else
            _core_sort_by { |a| a.__send__(*args, &block) }
        end
    end
end

if $0 == __FILE__
    class Factor
        attr_reader :number
        def initialize(number)
            @number = number
        end
        def apply_to(other_number)
            @number * other_number
        end
    end
    list = [Factor.new(-2), Factor.new(3)]
    p list.sort_by(:number)
    p list.sort_by(:apply_to, -1)
end
% ruby sort_by.rb
[#<Factor:0x401c0520 @number=-2>, #<Factor:0x401c050c @number=3>]
[#<Factor:0x401c050c @number=3>, #<Factor:0x401c0520 @number=-2>]

···

On Saturday 29 October 2005 18:00, Brian Schröder wrote:

On 29/10/05, Trans <transfire@gmail.com> wrote:
> Brian Schröder wrote:
> > If you are so bold, I have to chime in again to say that
> >
> > ary.sort_by :meth
> >
> > reads a lot better than
> >
> > ary.sort_by.meth
> >
> > because we don't do method chaining here.
> >
> > Sorry, could not resist. :wink:
>
> Ahhhhh... but can you do:
>
> ary.sort_by.meth(foo, bar, wack!)
>
> T.

And what does that mean and how often do I need it?

--
Stefan

What is so bad about

ary.sort_by { | it | it.meth(foo, bar, wack!) }

Then you can even make your sourcecode readable by putting information
into the it

playlist.sort_by { | track | track.meth(foo, bar, wack!) }
instead of
playlist.sort_by { it.meth(foo, bar, wack!) }

where it is unclear what the it is.

regards,

Brian

···

On 29/10/05, Dominik Bathon <dbatml@gmx.de> wrote:

On Sat, 29 Oct 2005 17:52:05 +0200, Trans <transfire@gmail.com> wrote:

>
> Brian Schröder wrote:
>> If you are so bold, I have to chime in again to say that
>>
>> ary.sort_by :meth
>>
>> reads a lot better than
>>
>> ary.sort_by.meth
>>
>> because we don't do method chaining here.
>>
>> Sorry, could not resist. :wink:
>
> Ahhhhh... but can you do:
>
> ary.sort_by.meth(foo, bar, wack!)

So, what we really need is the implicit block variable, because it can do
all that :wink:

ary.sort_by { it.meth }

ary.sort_by { it.meth(foo, bar, wack!) }

and even:

ary.sort_by { some_hash[it] }

Dominik

--
http://ruby.brian-schroeder.de/

Stringed instrument chords: http://chordlist.brian-schroeder.de/

Dominik Bathon wrote:

So, what we really need is the implicit block variable, because it can do
all that :wink:

ary.sort_by { it.meth }

ary.sort_by { it.meth(foo, bar, wack!) }

and even:

ary.sort_by { some_hash[it] }

Uh oh...

  ary.whatever_by { |it1, it2| ... }

Now we're one step away from the from "ducking" it all:

  ary.whatever_by { %1.foo ... %2.bar }

T.

ary.sort_by(:foo, :bar, :wack!)

Oh, those weren't method calls, there were parameters to #meth --I know
Enumerable doesn't support args, but I have a module EnumerableArgs
that does (And let me tell you that was a pain --I had to essentially
rewrite Enumerable from scratch!)

T.

Hi --

···

On Sun, 30 Oct 2005, Brian Schröder wrote:

On 29/10/05, Dominik Bathon <dbatml@gmx.de> wrote:

On Sat, 29 Oct 2005 17:52:05 +0200, Trans <transfire@gmail.com> wrote:

Brian Schröder wrote:

If you are so bold, I have to chime in again to say that

  ary.sort_by :meth

reads a lot better than

  ary.sort_by.meth

because we don't do method chaining here.

Sorry, could not resist. :wink:

Ahhhhh... but can you do:

   ary.sort_by.meth(foo, bar, wack!)

So, what we really need is the implicit block variable, because it can do
all that :wink:

ary.sort_by { it.meth }

ary.sort_by { it.meth(foo, bar, wack!) }

and even:

ary.sort_by { some_hash[it] }

Dominik

What is so bad about

ary.sort_by { | it | it.meth(foo, bar, wack!) }

Then you can even make your sourcecode readable by putting information
into the it

playlist.sort_by { | track | track.meth(foo, bar, wack!) }
instead of
playlist.sort_by { it.meth(foo, bar, wack!) }

where it is unclear what the it is.

I agree; I think a loop/block variable with a real name is a step
forward from one without one. And yes, you could do:

   track = it

but I'd rather not have the problem to solve in the first place. I
think it's been solved already by Matz :slight_smile:

David

--
David A. Black
dblack@wobblini.net

Brian Schröder wrote:

···

On 29/10/05, Dominik Bathon <dbatml@gmx.de> wrote:

On Sat, 29 Oct 2005 17:52:05 +0200, Trans <transfire@gmail.com> wrote:

Brian Schröder wrote:

If you are so bold, I have to chime in again to say that

ary.sort_by :meth

reads a lot better than

ary.sort_by.meth

because we don't do method chaining here.

Sorry, could not resist. :wink:

Ahhhhh... but can you do:

  ary.sort_by.meth(foo, bar, wack!)

So, what we really need is the implicit block variable, because it can do
all that :wink:

ary.sort_by { it.meth }

ary.sort_by { it.meth(foo, bar, wack!) }

and even:

ary.sort_by { some_hash[it] }

Dominik

What is so bad about

ary.sort_by { | it | it.meth(foo, bar, wack!) }

Then you can even make your sourcecode readable by putting information
into the it

playlist.sort_by { | track | track.meth(foo, bar, wack!) }
instead of
playlist.sort_by { it.meth(foo, bar, wack!) }

where it is unclear what the it is.

regards,

Brian

I agree. `it' would seem to refer to the playlist itself, not the playlist item. This would be good too:

   playlist.sort_by :track_length

Cheers,
Daniel

What is so bad about

ary.sort_by { | it | it.meth(foo, bar, wack!) }

Nothing, it is just more typing...

Then you can even make your sourcecode readable by putting information
into the it

playlist.sort_by { | track | track.meth(foo, bar, wack!) }
instead of
playlist.sort_by { it.meth(foo, bar, wack!) }

where it is unclear what the it is.

I know that it is not very clear, you have to get the meaning from the context.
The implicit version would just be for short blocks, where the meaning is obvious from the context.
I don't want to remove the named block parameters, just offer an alternative.

Dominik

···

On Sat, 29 Oct 2005 20:02:51 +0200, Brian Schröder <ruby.brian@gmail.com> wrote:

Maybe

ary.whatever_by { it[0].foo ... it[1].bar }

because block variables use multiple assignment anyway.

But I would use the implicit version only for really simple things. If there are multiple block variables, then please name them.

Dominik

···

On Sat, 29 Oct 2005 21:07:05 +0200, Trans <transfire@gmail.com> wrote:

Dominik Bathon wrote:

So, what we really need is the implicit block variable, because it can do
all that :wink:

ary.sort_by { it.meth }

ary.sort_by { it.meth(foo, bar, wack!) }

and even:

ary.sort_by { some_hash[it] }

Uh oh...

  ary.whatever_by { |it1, it2| ... }

Now we're one step away from the from "ducking" it all:

  ary.whatever_by { %1.foo ... %2.bar }

ary.sort_by(:foo, :bar, :wack!)

Oh, those weren't method calls, there were parameters to #meth --I

Then I guess this makes a good indication how cryptic that was... :slight_smile:

know Enumerable doesn't support args, but I have a module
EnumerableArgs that does (And let me tell you that was a pain --I had
to essentially rewrite Enumerable from scratch!)

What do you mean by "Enumerable doesn't support args"? It's a module not a method. What do you need those args for?

Cheers

    robert

···

Trans <transfire@gmail.com> wrote:

Robert Klemme wrote:

>> ary.sort_by(:foo, :bar, :wack!)
>
> Oh, those weren't method calls, there were parameters to #meth --I

Then I guess this makes a good indication how cryptic that was... :slight_smile:

> know Enumerable doesn't support args, but I have a module
> EnumerableArgs that does (And let me tell you that was a pain --I had
> to essentially rewrite Enumerable from scratch!)

What do you mean by "Enumerable doesn't support args"? It's a module not a
method. What do you need those args for?

A good exmaple is ObjectSpace:

  class << ObjectSpace
    include EnumerableArgs
    alias :each, :each_object
  end

  ObjectSpace.select(Class) { |c| c.name =~ /^S/ }

You can't do this with regular Enumerable b/c you cannot pass any
arguments through the enumerable methods to the underlying #each
method.

(Sorry if there are any bugs in the code. Ruby's not installed on my
system at the moment.)

T.

···

Trans <transfire@gmail.com> wrote:

Trans schrieb:

Robert Klemme wrote:

What do you mean by "Enumerable doesn't support args"? It's a module not a
method. What do you need those args for?

A good exmaple is ObjectSpace:

  class << ObjectSpace
    include EnumerableArgs
    alias :each, :each_object
  end

  ObjectSpace.select(Class) { |c| c.name =~ /^S/ }

You can't do this with regular Enumerable b/c you cannot pass any
arguments through the enumerable methods to the underlying #each
method.

require "enumerator"

ObjectSpace.enum_for(:each_object, Class).select { |c| c.name =~ /^S/ }

Regards,
Pit

Pit Capitain wrote:

require "enumerator"

ObjectSpace.enum_for(:each_object, Class).select { |c| c.name =~ /^S/ }

He he! Yep, I'd call that "inflation" alright ;). Better yet, now we've
almost come full circle since #enum_for works in essentially the same
way as #every !

Don't you just love it David!? :wink:

Thanks but no thanks. I'll take an Enumerable with args and a
elementwise operator over this any day.

T.

Why? Do you have a good reason beyond the fact that you took all the
time to write your Enumerable with args? The enumerator code makes
perfect sense to me, and requires less code than your version. Plus
you are modifying a core class, which is generally looked upon as a
bad practice.

Ryan

···

On 10/30/05, Trans <transfire@gmail.com> wrote:

Pit Capitain wrote:
>
> require "enumerator"
>
> ObjectSpace.enum_for(:each_object, Class).select { |c| c.name =~ /^S/ }

He he! Yep, I'd call that "inflation" alright ;). Better yet, now we've
almost come full circle since #enum_for works in essentially the same
way as #every !

Don't you just love it David!? :wink:

Thanks but no thanks. I'll take an Enumerable with args and a
elementwise operator over this any day.

Hi Ryan,

I didn't modify a core class. EnumerableArgs is a complete rewrite of
Enumerable and stands on it own.

The Enumerator approach lacks for a few reasons.

  1. Its' longer and less readable.
  2. It's creates an intermedeary object.
  4. It means Enumerable remains less useful.

While Enumerator may have it's uses, I do not find it's use here
"Ruby-esque", for much the same reason David disliked #every --I have
to agree.

T.

Hi Ryan,

I didn't modify a core class. EnumerableArgs is a complete rewrite of
Enumerable and stands on it own.

Sorry I was ambiguous. In the ObjectSpace example you modify a core class:

class << ObjectSpace
  include EnumerableArgs
  alias :each, :each_object
end

ObjectSpace.select(Class) { |c| c.name =~ /^S/ }

Though this modification is less troublesome than other kinds of changes.

The Enumerator approach lacks for a few reasons.

  1. Its' longer and less readable.

Here is the code from Pit:

require "enumerator"

ObjectSpace.enum_for(:each_object, Class).select { |c| c.name =~ /^S/ }

How is that longer than your example above? If the unneeded whitespace
is removed from both, your example is about 50 characters longer (this
is ignoring the needed "require" line, since your example lacks it.)

And maybe I'm "in too deep" in understanding Ruby, but I find the
above very readable (but I also find inject easy to understand now
too, so...)

And while your example is also pretty easy to read, it requires some
knowledge as to what EnumerableArgs is, since the name isn't totally
obvious.

  2. It's creates an intermedeary object.

Fair enough.

  4. It means Enumerable remains less useful.

What happened to number 3? :wink:

I'm not arguing that the code you made doesn't have some merit, I just
don't agree that it is any better than the enumerator example.

While Enumerator may have it's uses, I do not find it's use here
"Ruby-esque", for much the same reason David disliked #every --I have
to agree.

Well it seems the perspective of something being "Ruby-esque" is quite
subjective, since I find the enumerator example quite "Ruby-esque."

Regards,
Ryan

···

On 10/30/05, Trans <transfire@gmail.com> wrote:

Hi Ryan,

I didn't modify a core class. EnumerableArgs is a complete rewrite of
Enumerable and stands on it own.

The Enumerator approach lacks for a few reasons.

1. Its' longer and less readable.

Can't see this.

2. It's creates an intermedeary object.

As all the "every" approaches that promote method chaining. Those might even create multiple proxy objects depending on the implementation.

4. It means Enumerable remains less useful.

I cannot see this.

While Enumerator may have it's uses, I do not find it's use here
"Ruby-esque", for much the same reason David disliked #every --I have
to agree.

The big advantage of Enumerator is that it implements Enumerable thus allowing all code created for Enumerables to work with an Enumerator instance. The big disadvantage of your approach OTOH is that you modify an Enumerable instance and thus shadowing methods defined in Enumerable. This may lead to unexpected effects if people rely on the original Enumerable methods. Also, how many classes do you know that have method #each that accepts arguments? I'd go even further than Ryan, I think Enumerator is better in this case than your approach.

Cheers

    robert

···

Trans <transfire@gmail.com> wrote:

Robert what are you talking about? "You can't see" isn't a useful
argument. And your concept of EnumerableArgs seems distorted. Its just
a Mixin. Use where you want to or don't, it doesn't effect anything
else.

T.

Trans wrote:

Robert what are you talking about? "You can't see" isn't a useful
argument.

I didn't want to repeat Ryan's argument and just signal my approval.
Should've been clearer.

And your concept of EnumerableArgs seems distorted. Its just
a Mixin. Use where you want to or don't, it doesn't effect anything
else.

I don't think my concept of EA is distorted: even a mixin can affect other
code. As I said, code that relies on the std Enumerable behavior might be
affected if handed an instance that mixes in EA. I don't say it will but
there's a chance because EA redefines methods from Enumerable.

The other potential issue is that EA's methods may be hidden because it's
a mixin...

What about my last question? How many classes do you know that have
method #each that accepts arguments?

Kind regards

    robert

As I said, code that relies on the std Enumerable behavior
might be affected if handed an instance that mixes in EA.
I don't say it will but there's a chance because EA
redefines methods from Enumerable.

Such an argument smakes dead against the whole concept of polymorphism
and duck-typing.

What about my last question? How many classes do you
know that have method #each that accepts arguments?

That's a ridiculous question. The reason you don't have ANY #each
taking parameters is becuase it won't work with Enumerable!

This whole discussion is silly. If you really think this is "longer" or
"less readable"

  ObjectSpace.select(Class) { |c| c.name =~ /^S/ }

Than this:

  ObjectSpace.enum_for(:each_object, Class).select { |c| c.name =~ /^S/
}

Then by all means, type away.

T.