Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

DRYing a Regex

2 views
Skip to first unread message

RichardOnRails

unread,
Nov 12, 2009, 5:58:22 PM11/12/09
to
I've got a routine that works fine at building an array of upper-case
strings extracted from a string:

aNewList = []
s = StringScanner.new sNewList
upper = /[A-Z]+/
not_upper= Regexp.new( upper.source.sub( /\[/, '[^' ) )
while not s.eos?
case
when s.skip(upper); aNewList << s.matched
else s.skip(not_upper)
end
end

But the not_upper Regexp definition is really a kludge. It somewhat
camouflages what is really /[^A-Z]+/

I'd like to DRY it by expressing it as something like !upper. I need
something like !~ we use normally with string searches.

Any ideas?
--
Richard

Caleb Clausen

unread,
Nov 12, 2009, 6:24:15 PM11/12/09
to
On 11/12/09, RichardOnRails

<RichardDummy...@uscomputergurus.com> wrote:
> upper = /[A-Z]+/
> not_upper= Regexp.new( upper.source.sub( /\[/, '[^' ) )
[snip]

> But the not_upper Regexp definition is really a kludge. It somewhat
> camouflages what is really /[^A-Z]+/
>
> I'd like to DRY it by expressing it as something like !upper. I need
> something like !~ we use normally with string searches.

This is somewhat better, but still not real obvious:
not_upper=/(?:.(?!#{upper}))+/ #untested, tho

Myself, I'd just write not_upper=/[^A-Z]/.... for something this
short, is it really worth trying all that hard to be DRY?

James Edward Gray II

unread,
Nov 12, 2009, 6:50:09 PM11/12/09
to

Well, you don't really need a StringScanner for this simple task. Your code really just rebuilds String#scan():

a_new_list = s_new_list.scan(/[A-Z]+/)

Note that I've also switched your variable naming style to the snake_case that we Rubyists prefer.

Hope that helps.

James Edward Gray II

Caleb Clausen

unread,
Nov 12, 2009, 7:34:20 PM11/12/09
to
On 11/12/09, RichardOnRails
<RichardDummy...@uscomputergurus.com> wrote:
> I've got a routine that works fine at building an array of upper-case
> strings extracted from a string:
>
> aNewList = []
> s = StringScanner.new sNewList
> upper = /[A-Z]+/
> not_upper= Regexp.new( upper.source.sub( /\[/, '[^' ) )
> while not s.eos?
> case
> when s.skip(upper); aNewList << s.matched
> else s.skip(not_upper)
> end
> end

OTOH, you can rewrite it like this, and not have to even mention the
complement of the match you're interested in:

aNewList = []
s = StringScanner.new sNewList
upper = /[A-Z]+/

aNewList<< s.matched while s.skip_until(upper)

(Not tested real thoroughly, corner cases may break.)

Caleb Clausen

unread,
Nov 12, 2009, 7:37:20 PM11/12/09
to
On 11/12/09, James Edward Gray II <ja...@graysoftinc.com> wrote:
> Well, you don't really need a StringScanner for this simple task. Your code
> really just rebuilds String#scan():
>
> a_new_list = s_new_list.scan(/[A-Z]+/)

ooh! that's even better.

RichardOnRails

unread,
Nov 13, 2009, 12:58:56 AM11/13/09
to

You're right. I didn't NEED to DRY that simple thing. I'm just
trying to improve my coding generally, especially to write things that
don't break easily when the inevitable changes are made.

But cutting out 90% of the code, wow! That's DRY!!

Thank you very much for your ideas. I haven't tested it yet, but it
looks right to me.

Best wishes,
Richard

RichardOnRails

unread,
Nov 13, 2009, 1:20:30 AM11/13/09
to
On Nov 12, 6:50 pm, James Edward Gray II <ja...@graysoftinc.com>
wrote:

Hi James,

As I said to Caleb, cutting my 10-liner down to 1 is extreme DRYing!!
Thanks for that.

As far as underscoring vs. Camel-case goes, I know Rubyists'
preference, but I bow to Shakespeare's notion that "a rose by any
other name is just as sweet." I spent a couple decades writing/
maintaining Window's application for clients using C and C++, so I've
a fondness for Polish notation (at least that's what I think it was
called.) Typing extra hyphens vs pressing the shift key lets me write
code faster, and the a/s/h prefix for arrays/strings/hashes helps me
avoid a lot of interpreter complaints. And fellow programmers of
almost any stripe knows what I mean. Finally, I retired curmudgeon,
and you know how we old folks are :-)

Seriously, your insight was very helpful and will help me avoid a
bunch of wasteful code.

Best wishes,
Richard

RichardOnRails

unread,
Nov 13, 2009, 2:34:22 AM11/13/09
to
Hey Caleb & James,

With your insights, I was able to cut down 18 lines of somewhat
obscure code to 6 lines that I find very readable. That's such and
improvement on the quality of the code.

Though I expect you guys are tired ot this thread, I included the new
and old code below, along with results that both of them produce.

Again, thank you very much for your insights.

Best wishes,
Richard


# Accept a new list as a string; extract an array of contiguous upper-
case letters as stock symbols, ignoring any duplicates (Test data)
# Delete any symbol in the current list that occurs here
sNewList = %{TMxxx CSCO COL INTC BRCM FDX AA CAT BUR FSLR MSFT',
PNC HPQ CSCO AMAT ORCL FCX ABX PVTB XHB CSCO TM FDX}

#===============
# New technique
#===============
aRawNewList = sNewList.scan(/[A-Z]+/)
aNewList = Set.new(aRawNewList ).to_a.sort
nDeleted = 0
aNewList.each { |sym| hCurrentList.delete sym and nDeleted += 1 if
hCurrentList[sym] }
show_array( aNewList, 10, "New List (unique:%d, dups:%d, deleted:%s)"
%
[aNewList.size, aRawNewList.size - aNewList.size, nDeleted] , true)

#============================
# Old technique; No longer used
#============================


aNewList = []
s = StringScanner.new sNewList
upper = /[A-Z]+/

non_upper= Regexp.new( upper.source.sub( /\[/, '[^' ) )
nNewSyms = nCurrSymsDeleted = 0


while not s.eos?
case
when s.skip(upper)

nNewSyms+=1
aNewList << s.matched unless aNewList.include? s.matched
( hCurrentList.delete s.matched and nCurrSymsDeleted += 1) if
hCurrentList[s.matched]
else
s.skip(non_upper)
end
end
show_array( aNewList.sort, 10, "New List (%d unique; %d dups; %d curr.
deleted)" %
[aNewList.size, nNewSyms - aNewList.size, nCurrSymsDeleted] )

#=======
# Output
#=======
===== New List (unique:19, dups:4, deleted:3) =====
AA ABX AMAT BRCM BUR CAT COL CSCO FCX FDX
FSLR HPQ INTC MSFT ORCL PNC PVTB TM XHB
===== =====

Robert Klemme

unread,
Nov 13, 2009, 5:10:31 AM11/13/09
to
2009/11/13 RichardOnRails <RichardDummy...@uscomputergurus.com>:

> As far as underscoring vs. Camel-case goes,  I know Rubyists'
> preference, but I bow to Shakespeare's notion that "a rose by any
> other name is just as sweet."  I spent a couple decades writing/
> maintaining Window's application for clients using C and C++, so I've
> a fondness for Polish notation (at least that's what I think it was
> called.)

I believe you mean Hungarian Notation:
http://en.wikipedia.org/wiki/Polish_notation

> Typing extra hyphens vs pressing the shift key lets me write
> code faster, and the a/s/h prefix for arrays/strings/hashes helps me
> avoid a lot of interpreter complaints.  And fellow programmers of
> almost any stripe knows what I mean.

There's always something to be said for conventions. The issue with
your notation is that it seems to be far less used among Ruby
programmers than the snake case. Snake case for variables and methods
also has the added advantage that classes and modules stand out
immediately.

Side note: with modern IDE's I believe there is not much reason to use
Hungarian Notation any more. I personally find it more difficult to
spot certain variables when all variables of the same type start with
the same letter. For me, PN actually _reduces_ readability.

>  Finally,  I retired curmudgeon,
> and you know how we old folks are :-)

LOL

Cheers

robert


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

Mark Thomas

unread,
Nov 13, 2009, 7:29:42 AM11/13/09
to
On Nov 13, 2:34 am, RichardOnRails

<RichardDummyMailbox58...@USComputerGurus.com> wrote:
> Hey Caleb & James,
>
> With your insights,  I was able to cut down 18 lines of somewhat
> obscure code to 6 lines that I find very readable.  That's such and
> improvement on the quality of the code.

I believe you can go further. For example, these three lines:

sNewList = %{TMxxx CSCO COL INTC BRCM FDX AA CAT BUR FSLR MSFT',
PNC HPQ CSCO AMAT ORCL FCX ABX PVTB XHB CSCO TM FDX}

aRawNewList = sNewList.scan(/[A-Z]+/)
aNewList = Set.new(aRawNewList ).to_a.sort

can be replaced by one:

aNewList = %W{TMxxx CSCO COL INTC BRCM FDX AA CAT BUR FSLR MSFT',


PNC HPQ CSCO AMAT ORCL FCX ABX PVTB XHB CSCO TM FDX}

(you can add .sort to the end but I don't think you need it)

also, consider something like this:

hCurrentList.delete_if { |key,v| aNewList.include?key }

-- Mark.

Robert Klemme

unread,
Nov 13, 2009, 9:17:39 AM11/13/09
to
2009/11/13 Mark Thomas <ma...@thomaszone.com>:

> On Nov 13, 2:34 am, RichardOnRails
> <RichardDummyMailbox58...@USComputerGurus.com> wrote:
>> Hey Caleb & James,
>>
>> With your insights,  I was able to cut down 18 lines of somewhat
>> obscure code to 6 lines that I find very readable.  That's such and
>> improvement on the quality of the code.
>
> I believe you can go further. For example, these three lines:
>
>  sNewList = %{TMxxx CSCO COL INTC BRCM FDX AA CAT BUR FSLR MSFT',
>               PNC HPQ CSCO AMAT ORCL FCX ABX PVTB XHB CSCO TM FDX}
>  aRawNewList = sNewList.scan(/[A-Z]+/)
>  aNewList = Set.new(aRawNewList ).to_a.sort
>
> can be replaced by one:
>
>  aNewList = %W{TMxxx CSCO COL INTC BRCM FDX AA CAT BUR FSLR MSFT',
>                PNC HPQ CSCO AMAT ORCL FCX ABX PVTB XHB CSCO TM FDX}

I don't think so because that appears to be input from the outside
which is provided as single String.

> (you can add .sort to the end but I don't think you need it)
>
> also, consider something like this:
>
>  hCurrentList.delete_if { |key,v| aNewList.include?key }

Basically the question is which of the two is larger. But if you do
it this way round (i.e. iterate the Hash and check for existence in
the new list then that should definitively be a Set).

Here's my suggestion

require 'set'

# dumy base
current = {"CSCO" => 1, "COL" => 2, "INTC" => 3, "BRCM" => 4, "FOO" => 99}

# user input
input = %{TMxxx CSCO COL INTC BRCM FDX AA CAT BUR FSLR MSFT


PNC HPQ CSCO AMAT ORCL FCX ABX PVTB XHB CSCO TM FDX}

# algorithm
symbols = input.scan(/[A-Z]+/)
deduped = symbols.to_set

old_size = current.size
deduped.each {|sym| current.delete sym}

p(deduped.sort,
10,
sprintf("New List (unique:%d, dups:%d, deleted:%s)",
deduped.size,
symbols.size - deduped.size,
old_size - current.size),
true)

p current

Marnen Laibow-Koser

unread,
Nov 13, 2009, 11:11:07 PM11/13/09
to
RichardOnRails wrote:
[...]

> As far as underscoring vs. Camel-case goes, I know Rubyists'
> preference, but I bow to Shakespeare's notion that "a rose by any
> other name is just as sweet."

It doesn't work that way in programming. Good naming practices are an
important part of readable code. This is particularly so in a language
like Ruby, in which "literate" interfaces are common.

I spent a couple decades writing/
> maintaining Window's application for clients using C and C++, so I've
> a fondness for Polish notation (at least that's what I think it was
> called.)

Polish Notation is Łukasiewicz-style prefix notation, rather like what's
used in Lisp. You mean Hungarian Notation.

But in any case, *you've been had*. Hungarian Notation as developed by
Charles Simonyi is extremely useful in non-OO code (I've used it in PHP
with great success). Hungarian Notation as the term is usually
understood is a very stupid thing indeed, which has unfortunately been
foisted by Microsoft on huge numbers of Windows programmers who really
should know better. :) It is (at best) marginally useful in statically
typed languages like C, and downright misleading in dynamically typed
languages like Ruby.

The difference is that Simonyi's original concept encodes information
*outside the scope* of the variable's type (which, after all, the
interpreter or compiler already knows about). For example, in a mapping
system, you might have kmDistance and ftCorrection. It's entirely clear
from those names that kmDistance + ftCorrection would be adding
kilometers and feet without a conversion, and thus it's immediately
clear that that operation is wrong.

OTOH, legions of misled Windows developers would simply call those two
variables intDistance and intCorrection, incorporating no new useful
information and making the names harder to read.

For more on the misuse of Hungarian Notation, please see
http://www.joelonsoftware.com/articles/Wrong.html (Simonyi's original is
there called Apps Hungarian, while the popular perversion is called
Systems Hungarian). There's also some interesting discussion at
http://c2.com/cgi/wiki?HungarianNotation , if you can wade through the
disorganization.

Systems Hungarian, BTW, is bad enough in C, where you should be able to
refer to your variable declarations. If your functions are so long that
you can't refer easily to declarations, then you need to refactor to
shorter methods for overall readability anyway -- methods should be
short. Systems Hungarian has no use at all in Ruby, since although
objects are typed, variables are not, so it's perfectly possible to do
intValue = 1
# later
intValue = {:foo => 'bar'}

Even Apps Hungarian is not a great idea in OO code. Instead, just use
the type system, so that distance would be a Kilometer object and
correction would be a Foot object. Kilometer.+(foot) could then either
raise an exception or invoke a conversion.

In summary, then, Hungarian Notation of either sort is inappropriate in
Ruby. Drop the habit.

> Typing extra hyphens vs pressing the shift key lets me write
> code faster, and the a/s/h prefix for arrays/strings/hashes helps me
> avoid a lot of interpreter complaints.

If you care about removing characters from variable names, start with
removing the Hungarian warts. As I explained above, they serve no
useful purpose in Ruby at all. And I have to say, I don't find
wordsRunTogether as easy to read as words_with_underscores -- the
underscores look more like spaces and delineate the words better to my
eye. WouldYouRatherReadThisClauseHere, or
would_you_rather_read_this_clause_here?

In any case, "snake_case" is the prevailing style in Ruby, and virtually
every Ruby library uses it (including the standard library and Rails) --
your code will look strange if you don't follow suit. The examples in
Programming Ruby tend to use camelCase, but that's more of a flaw in the
book than an indicator of Ruby practice.

> And fellow programmers of
> almost any stripe knows what I mean. Finally, I retired curmudgeon,
> and you know how we old folks are :-)

Age is not an excuse. If you're going to learn a language, take the
time to learn the idioms and the "spirit" of the language, not just the
bare essentials of syntax. I've seen far too many people try to write
C, Java, or PHP in Ruby -- avoid the temptation!

>
> Seriously, your insight was very helpful and will help me avoid a
> bunch of wasteful code.
>
> Best wishes,
> Richard

Best,
--
Marnen Laibow-Koser
http://www.marnen.org
mar...@marnen.org
--
Posted via http://www.ruby-forum.com/.

James Edward Gray II

unread,
Nov 13, 2009, 11:21:13 PM11/13/09
to
On Nov 13, 2009, at 10:11 PM, Marnen Laibow-Koser wrote:

> RichardOnRails wrote:
> [...]
>> As far as underscoring vs. Camel-case goes, I know Rubyists'
>> preference, but I bow to Shakespeare's notion that "a rose by any
>> other name is just as sweet."
>
> It doesn't work that way in programming. Good naming practices are an
> important part of readable code.

As the saying goes, "When in Rome, do as the Romans do." You're speaking our language now and you want to learn to speak it like us, even with our slang. That allows you to communicate with us better so we can learn from each other.

> Even Apps Hungarian is not a great idea in OO code. Instead, just use
> the type system, so that distance would be a Kilometer object and
> correction would be a Foot object. Kilometer.+(foot) could then either
> raise an exception or invoke a conversion.

I would like to see us move away from considering classes to be types at all in Ruby. Who knows what modules an object has mixed into it and who knows what singleton methods are defined on it. A class, which is what people traditionally take for the type, is just one piece of an object's identity.

James Edward Gray II


Marnen Laibow-Koser

unread,
Nov 13, 2009, 11:27:51 PM11/13/09
to
James Edward Gray II wrote:
[...]

>> Even Apps Hungarian is not a great idea in OO code. Instead, just use
>> the type system, so that distance would be a Kilometer object and
>> correction would be a Foot object. Kilometer.+(foot) could then either
>> raise an exception or invoke a conversion.
>
> I would like to see us move away from considering classes to be types at
> all in Ruby. Who knows what modules an object has mixed into it and who
> knows what singleton methods are defined on it.

Do you make much use of singleton mixins or singleton methods in your
code? I know I don't.

> A class, which is what
> people traditionally take for the type, is just one piece of an object's
> identity.

You're right. But with a proper class system, my point about not
needing Apps Hungarian in Ruby still stands, I think. Do you disagree?

>
> James Edward Gray II

David Turnbull

unread,
Nov 13, 2009, 11:39:45 PM11/13/09
to
On 14/11/2009, at 15:21, James Edward Gray II <ja...@graysoftinc.com>
wrote:

>> Even Apps Hungarian is not a great idea in OO code. Instead, just
>> use
>> the type system, so that distance would be a Kilometer object and
>> correction would be a Foot object. Kilometer.+(foot) could then
>> either
>> raise an exception or invoke a conversion.
>
> I would like to see us move away from considering classes to be
> types at all in Ruby. Who knows what modules an object has mixed
> into it and who knows what singleton methods are defined on it. A
> class, which is what people traditionally take for the type, is just
> one piece of an object's identity.

I would still look immediately to the class of the object in order to
find out what it's supposed to do. From there, the class definition
will probably list it's module inclusions prominently.

As a vim user, with very limited interactive debugging, my primary
exploration technique will usually consist of at most a couple of
'obj.methods.grep' calls followed by grepping ~/gems which seems to
emphasize the actual reading of the source for object identity info.

Python's integrated documentation would be really welcome in this
case, i think. :)

I'm curious what you think the most correct way is to discover object
identity.

>

Marnen Laibow-Koser

unread,
Nov 13, 2009, 11:43:25 PM11/13/09
to
David Turnbull wrote:
> On 14/11/2009, at 15:21, James Edward Gray II <ja...@graysoftinc.com>
> wrote:
>> class, which is what people traditionally take for the type, is just
>> one piece of an object's identity.
>
> I would still look immediately to the class of the object in order to
> find out what it's supposed to do.

I would too. James is correct that it isn't the whole story, but it's
the best place to start.

> From there, the class definition
> will probably list it's module inclusions prominently.
>
> As a vim user, with very limited interactive debugging,

What? You can use ruby-debug interactively in a console session. I
often do.

> my primary
> exploration technique will usually consist of at most a couple of
> 'obj.methods.grep' calls followed by grepping ~/gems which seems to
> emphasize the actual reading of the source for object identity info.
>
> Python's integrated documentation would be really welcome in this
> case, i think. :)

WTF? Aren't you familiar with RDoc? And didn't you know that running
"gem server" will start a Web server with gem RDoc pages on port 8808?

>
> I'm curious what you think the most correct way is to discover object
> identity.

Object identity? Well, for that, you need object_id. That's something
different than object type.

Bill Kelly

unread,
Nov 14, 2009, 1:23:04 AM11/14/09
to

From: "David Turnbull" <dstur...@gmail.com>

> On 14/11/2009, at 15:21, James Edward Gray II <ja...@graysoftinc.com>
> wrote:
>> I would like to see us move away from considering classes to be
>> types at all in Ruby. Who knows what modules an object has mixed
>> into it and who knows what singleton methods are defined on it. A
>> class, which is what people traditionally take for the type, is just
>> one piece of an object's identity.
>
> I would still look immediately to the class of the object in order to
> find out what it's supposed to do. From there, the class definition
> will probably list it's module inclusions prominently.

A human looking to documentation to find out what an object
of a partiular class is supposed to *do*, is one thing. But
then there's the programmatic flipside where one could code
a method to select between different behaviors based on the
class-type of a given argument-object.

def foo(bar)
if bar.is_a? Array
do_array_thing(bar)
elsif bar.is_a? String
do_string_thing(bar)
else
... # ?
end
end

I believe it's (variations on) the above that are viewed
as unreasonably restrictive in ruby.

It's challenging, too, because even :respond_to? can be
misleading.

I like Og (Object Graph), an Object Relational Mapping
library in ruby providing high-level database access.

require 'og'

class Address
property :name, String
property :company, String
property :dept, String
property :addr1, String
property :addr2, String
property :city, String
property :state, String
property :zip, String
property :country, String
belongs_to :order, Order
end

When Og is initialized, it searches ObjectSpace for
classes like the above, and detects that they are
intended to be Og-managed classes, and imbues them
with certain basic features. (It also generates the
SQL needed to create the database tables
corresponding to such classes.)

An example is that, given nothing more than the above
Address class declaration... I could now say:

result = Address.find_by_name_and_state("Bob Jones", "CA")

But..! The Address.find_by_name_and_state doesn't even
exist until the time that it is called. Part of the
magic with which an Og-managed class is imbued, is
some method_missing logic which looks for particular
method signatures, like /find_by_(.*)/ , and, at the
moment such a method is called, is tested against the
following, behind the scenes:

def method_missing(sym, *args, &block)
if match = /find_(all_by|by)_([_a-zA-Z]\w*)/.match(sym.to_s)
return find_by_(match, args, &block)
elsif match = /find_or_create_by_([_a-zA-Z]\w*)/.match(sym.to_s)
return find_or_create_by_(match, args, &block)
else
super
end
end

(Note: In this case, it appears Og _always_ handles the
request via method_missing. But I've seen other code
in Og (or maybe Nitro) that did *define* the method when
it was first called, such that on subsequent invocations
the method would now already be existing.)

. . Anyway, the point being, Ruby is pretty dynamic.

:)


> Python's integrated documentation would be really welcome in this
> case, i think. :)

I seem to recall mention awhile back on ruby-talk of
a gem or module that integrated `ri` into `irb`, such
that one could pull up the documentation from within
irb. (I don't have any links for that, sorry.)


Regards,

Bill

Ralph Shnelvar

unread,
Nov 14, 2009, 7:03:06 AM11/14/09
to
BK> elsif bar.is_a? String

As a newbie I would surely like to know why the language decided on
"elsif" rather than "elseif".

And before anyone accuses me of not doing a Google search on the
subject ... I did. E.g. http://www.ruby-forum.com/topic/100350

I'm not trying to change the language ... I'm wondering if the
language developer(s) had a reason for it? Did the want to save the
typing of an 'e'?

- - - -

While we're at it, is there a (undocumented?) compiler switch that says "always check
for then in if/elsif statements"?

And before anyone accuses me of not doing a Google search on the
subject of compiler switches ... I did. E.g. http://www.zenspider.com/Languages/Ruby/QuickRef.html


Mark Thomas

unread,
Nov 14, 2009, 8:53:24 AM11/14/09
to
On Nov 14, 7:03 am, Ralph Shnelvar <ral...@dos32.com> wrote:
> BK>     elsif bar.is_a? String
>
> As a newbie I would surely like to know why the language decided on
> "elsif" rather than "elseif".

Because a precedent had been set in Perl. That's one of the
unfortunate Perlisms in Ruby.

At least Matz didn't borrow it from Bash, which uses "elif".

Todd Benson

unread,
Nov 14, 2009, 9:27:26 AM11/14/09
to
On Sat, Nov 14, 2009 at 6:03 AM, Ralph Shnelvar <ral...@dos32.com> wrote:
> BK> elsif bar.is_a? String
>
> As a newbie I would surely like to know why the language decided on
> "elsif" rather than "elseif".

I'm pretty sure it's a Perl artifact.

Rick DeNatale

unread,
Nov 14, 2009, 10:56:05 AM11/14/09
to
On Fri, Nov 13, 2009 at 11:39 PM, David Turnbull <dstur...@gmail.com> wrote:
> On 14/11/2009, at 15:21, James Edward Gray II <ja...@graysoftinc.com> wrote:
>>>
>>> Even Apps Hungarian is not a great idea in OO code.  Instead, just use
>>> the type system, so that distance would be a Kilometer object and
>>> correction would be a Foot object.  Kilometer.+(foot) could then either
>>> raise an exception or invoke a conversion.
>>
>> I would like to see us move away from considering classes to be types at
>> all in Ruby.  Who knows what modules an object has mixed into it and who
>> knows what singleton methods are defined on it.  A class, which is what
>> people traditionally take for the type, is just one piece of an object's
>> identity.
>
> I would still look immediately to the class of the object in order to find
> out what it's supposed to do. From there, the class definition will probably
> list it's module inclusions prominently.

I've long (since at least 18 years) been an advocate of divorcing the
notion of type from class in dynamically typed languages:

http://talklikeaduck.denhaven2.com/files/TypesFromTheClientsViewpoint.PDF

IMHO, viewing a variable as a 'role' to be filled with one or more
objects is a powerful technique.

Alastair Cockburn recently told me that he still refers clients to the
reference paper, which I wrote at IBM, when he and l were both there.

The view fits into a general approach to OO design which is called
"responsibility based" or "role based" design. Rebecca Wirfs-Brock
was one of the authors who published books on the approach

http://www.amazon.com/Rebecca-Wirfs-Brock/e/B001IQXNWC/ref=ntt_athr_dp_pel_1

This was before the static-typing crowd (starting with C++) took over
the conventional wisdom as to what it meant to be OO, in turn leading
to a proliferation of "methodologies" using static typing.

Those of us in the dynamic typing/roles/responsibility based community
see this as an unfortunate parallel to Gresham's Law.

With the re-birth of interest in dynamically typed languages I think
that the role based view is preferable.

--
Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Twitter: http://twitter.com/RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale

James Edward Gray II

unread,
Nov 14, 2009, 11:27:59 AM11/14/09
to
On Nov 14, 2009, at 12:23 AM, Bill Kelly wrote:

> (Note: In this case, it appears Og _always_ handles the
> request via method_missing. But I've seen other code
> in Og (or maybe Nitro) that did *define* the method when
> it was first called, such that on subsequent invocations
> the method would now already be existing.)

ActiveRecord from Rails works this way. If you would like to see the code it starts around line 1830 of this file:

http://github.com/rails/rails/blob/master/activerecord/lib/active_record/base.rb

> I seem to recall mention awhile back on ruby-talk of
> a gem or module that integrated `ri` into `irb`, such
> that one could pull up the documentation from within
> irb. (I don't have any links for that, sorry.)

Here's what I have in my .irbrc file:

def ri(*names)
system(%{ri #{names.join(" ")}})
end

James Edward Gray II


James Edward Gray II

unread,
Nov 14, 2009, 11:58:03 AM11/14/09
to
On Nov 13, 2009, at 10:27 PM, Marnen Laibow-Koser wrote:

> James Edward Gray II wrote:
> [...]
>>> Even Apps Hungarian is not a great idea in OO code. Instead, just use
>>> the type system, so that distance would be a Kilometer object and
>>> correction would be a Foot object. Kilometer.+(foot) could then either
>>> raise an exception or invoke a conversion.
>>
>> I would like to see us move away from considering classes to be types at
>> all in Ruby. Who knows what modules an object has mixed into it and who
>> knows what singleton methods are defined on it.
>
> Do you make much use of singleton mixins or singleton methods in your
> code? I know I don't.

I have been doing a lot more of mixing modules into individual objects, yes. I have been more than pleased with the results too. I think it's something we should all try to do more of. I gave a speech about this at LSRC this year which should show up here someday:

http://lsrc2009.confreaks.com/

I can give a couple of examples.

I recently ran across some code that had extensions to a core system. Each extension would reopen the core classes and edit away. Unfortunately, they had to duplicate a lot of the core code to make little changes to it. I rewrote the code to allow extensions to register modules with the core classes. Then when those classes produced objects, they would mix in any registered modules. This simple eliminated almost all of the duplication, because the modules were in the singleton class *in front of* the methods they were modifying. They could read the arguments and see if they needed to step in with their modified behavior, or just hand off to super().

I showed another example in my talk where I was trying to create a one instance configuration object. Originally I did it with a constant and some clever reopening of the singleton class, but that caused problems like not being able to easily document this object's API. I switched to just creating the one instance I needed and immediately mixing in a module that added the special functionality and it solved all the problems I had. You can document a module just fine. (The example is in my slides, if you want to see it: http://blog.grayproductions.net/articles/lone_star_rubyconf_slides.)

I think we should do more of this. For example, I think we could return an Array that mixes in a Paginated module instead of a PaginatedCollection object that inherits from Array. That feels more right to me. It's an Array and it has some extra functionality added in related to pagination. The uses go on and on.

>> A class, which is what
>> people traditionally take for the type, is just one piece of an object's
>> identity.
>
> You're right. But with a proper class system, my point about not
> needing Apps Hungarian in Ruby still stands, I think. Do you disagree?

I was agreeing with you, yes. I was saying that adding an a_ or s_ to the beginning of a variable name, assumably to indicate Array or String, is a damaging practice, because that's not necessarily all you need to know about the object. I think it promotes the wrong kind of thinking about Ruby's types.

James Edward Gray II


James Edward Gray II

unread,
Nov 14, 2009, 12:06:56 PM11/14/09
to
On Nov 13, 2009, at 10:39 PM, David Turnbull wrote:

> On 14/11/2009, at 15:21, James Edward Gray II <ja...@graysoftinc.com> wrote:
>>> Even Apps Hungarian is not a great idea in OO code. Instead, just use
>>> the type system, so that distance would be a Kilometer object and
>>> correction would be a Foot object. Kilometer.+(foot) could then either
>>> raise an exception or invoke a conversion.
>>
>> I would like to see us move away from considering classes to be types at all in Ruby. Who knows what modules an object has mixed into it and who knows what singleton methods are defined on it. A class, which is what people traditionally take for the type, is just one piece of an object's identity.
>
> I would still look immediately to the class of the object in order to find out what it's supposed to do. From there, the class definition will probably list it's module inclusions prominently.

Sure, it's definitely part of the picture.

> As a vim user, with very limited interactive debugging, my primary exploration technique will usually consist of at most a couple of 'obj.methods.grep' calls followed by grepping ~/gems which seems to emphasize the actual reading of the source for object identity info.

Your use of grep() for methods catches a lot of things a class definition might not tell you.

> I'm curious what you think the most correct way is to discover object identity.

Well, if we just mix modules into objects as I recommended in my previous message, Ruby's type system just naturally handles all of the details.

>> o = Object.new
=> #<Object:0x10037f9f0>
>> module Magical
>> def inspect
>> "#<MagicalObject ##{object_id}>"
>> end
>> end
=> nil
>> o.extend(Magical)
=> #<MagicalObject #2149317880>
>> o.is_a? Object
=> true
>> o.is_a? Magical
=> true
>> class << o; self end.ancestors
=> [Magical, Object, Kernel]

James Edward Gray II


David A. Black

unread,
Nov 14, 2009, 1:21:41 PM11/14/09
to
Hi --

On Sat, 14 Nov 2009, Marnen Laibow-Koser wrote:

> James Edward Gray II wrote:
> [...]
>>> Even Apps Hungarian is not a great idea in OO code. Instead, just use
>>> the type system, so that distance would be a Kilometer object and
>>> correction would be a Foot object. Kilometer.+(foot) could then either
>>> raise an exception or invoke a conversion.
>>
>> I would like to see us move away from considering classes to be types at
>> all in Ruby. Who knows what modules an object has mixed into it and who
>> knows what singleton methods are defined on it.
>
> Do you make much use of singleton mixins or singleton methods in your
> code? I know I don't.

I write class methods sometimes (I know those are singleton with an
asterisk next to them, but still), and I think that extending core
objects with modules is a frequently overlooked and very powerful
alternative to reopening core classes and adding methods.

This kind of thing:

class String
def method_I_need_once_or_twice
...

is almost always overkill. It's sort of the core-functionality
counterpart of using global variables. Extending an object is a much
more precise operation -- and has the additional merit, I find, of
really making you think about whether it's worth bothering to the
extend the object instead of working with what the object can already
do.


David

--
The Ruby training with D. Black, G. Brown, J.McAnally
Compleat Jan 22-23, 2010, Tampa, FL
Rubyist http://www.thecompleatrubyist.com

David A. Black/Ruby Power and Light, LLC (http://www.rubypal.com)

David A. Black

unread,
Nov 14, 2009, 1:27:39 PM11/14/09
to
Hi --

Indeed:

$ ./script/console
Loading development environment (Rails 2.3.3)
>> c = Container.first
=> #<Container id: 1, name: "stuff", created_at: "2009-09-14
20:51:19", updated_at: "2009-09-14 20:51:19">
>> items = c.items
=> [#<Item id: 345698075, collection_id: 1, created_at: "2009-09-16
22:29:41", updated_at: "2009-09-16 22:47:27", description: "abc">]
>> items.class
=> Array
>> items.respond_to?(:find)
=> true
>> items.find {|item| item.description == "abc" }
ActiveRecord::RecordNotFound: Couldn't find Item without an ID

Here, it's all about the documented interface, not the class name and
not the method names.

Marnen Laibow-Koser

unread,
Nov 14, 2009, 2:08:55 PM11/14/09
to
James Edward Gray II wrote:
[...]
>
> Here's what I have in my .irbrc file:
>
> def ri(*names)
> system(%{ri #{names.join(" ")}})
> end
>
> James Edward Gray II

Great idea!

Marnen Laibow-Koser

unread,
Nov 14, 2009, 2:18:44 PM11/14/09
to
James Edward Gray II wrote:
> On Nov 13, 2009, at 10:27 PM, Marnen Laibow-Koser wrote:
>
>>
>> Do you make much use of singleton mixins or singleton methods in your
>> code? I know I don't.
>
> I have been doing a lot more of mixing modules into individual objects,
> yes. I have been more than pleased with the results too. I think it's
> something we should all try to do more of.

How do you keep such code maintainable? It seems to me that
circumventing the class system on a regular basis makes it harder to
tell what's what.

> I gave a speech about this
> at LSRC this year which should show up here someday:
>
> http://lsrc2009.confreaks.com/
>
> I can give a couple of examples.
>
> I recently ran across some code that had extensions to a core system.
> Each extension would reopen the core classes and edit away.
> Unfortunately, they had to duplicate a lot of the core code to make
> little changes to it. I rewrote the code to allow extensions to
> register modules with the core classes. Then when those classes
> produced objects, they would mix in any registered modules. This simple
> eliminated almost all of the duplication, because the modules were in
> the singleton class *in front of* the methods they were modifying. They
> could read the arguments and see if they needed to step in with their
> modified behavior, or just hand off to super().

Yes, I can see how this would be useful to modify singleton objects.
But where a class has more than one or two instances, wouldn't this be
extremely confusing?

>
> I showed another example in my talk where I was trying to create a one
> instance configuration object. Originally I did it with a constant and
> some clever reopening of the singleton class, but that caused problems
> like not being able to easily document this object's API. I switched to
> just creating the one instance I needed and immediately mixing in a
> module that added the special functionality and it solved all the
> problems I had. You can document a module just fine. (The example is
> in my slides, if you want to see it:
> http://blog.grayproductions.net/articles/lone_star_rubyconf_slides.)
>

I'll take a look.

> I think we should do more of this. For example, I think we could return
> an Array that mixes in a Paginated module instead of a
> PaginatedCollection object that inherits from Array. That feels more
> right to me. It's an Array and it has some extra functionality added in
> related to pagination. The uses go on and on.

That feels hackish to me. If a common type like Array -- of which there
could be hundreds of instances in a typical program -- needs extra
functionality on some instances, it seems clearer and more
intention-revealing to subclass it. Where's the benefit of the
singleton mixin here?

>
>>> A class, which is what
>>> people traditionally take for the type, is just one piece of an object's
>>> identity.
>>
>> You're right. But with a proper class system, my point about not
>> needing Apps Hungarian in Ruby still stands, I think. Do you disagree?
>
> I was agreeing with you, yes. I was saying that adding an a_ or s_ to
> the beginning of a variable name, assumably to indicate Array or String,
> is a damaging practice, because that's not necessarily all you need to
> know about the object. I think it promotes the wrong kind of thinking
> about Ruby's types.

That's Systems Hungarian. Check out Joel's article if you haven't
already.

Best,
--
Marnen Laibow-Koser
http://www.marnen.org
mar...@marnen.org
>

> James Edward Gray II

Marnen Laibow-Koser

unread,
Nov 14, 2009, 2:40:18 PM11/14/09
to
David A. Black wrote:
[...]

> I write class methods sometimes (I know those are singleton with an
> asterisk next to them, but still),

Sometimes they're the right thing...

> and I think that extending core
> objects with modules is a frequently overlooked and very powerful
> alternative to reopening core classes and adding methods.
>
> This kind of thing:
>
> class String
> def method_I_need_once_or_twice
> ...
>
> is almost always overkill. It's sort of the core-functionality
> counterpart of using global variables.

True. (Even though I occasionally do this.)

> Extending an object is a much
> more precise operation -- and has the additional merit, I find, of
> really making you think about whether it's worth bothering to the
> extend the object instead of working with what the object can already
> do.
>

Agreed. But why is it worth extending an object instead of subclassing
it?

In other words,
class SpecialString < String
def method_I_need_once_or_twice
...
end
..
@some_string = SpecialString.new(@some_string)

seems to me like the right way to do this -- you can check if a given
String instance is a SpecialString or a plain String, or simply use
overriding and polymorphism for delegation.

Yet, if I understand you correctly, you and James are claiming that it
is preferable to do
class << @some_string
def method_I_need_once_or_twice
end

Do I understand correctly? If so, why? I hesitate to contradict such
experts as you and James, but I'm really not seeing the benefit. For
one thing, type-checking completely falls down with this pattern.
Polymorphism may or may not, depending on how it's implemented. And I
don't see a single advantage that we get in return for the loss of
type-checking. What am I missing?

Best,
--
Marnen Laibow-Koser
http://www.marnen.org
mar...@marnen.org
>

> David
>
> --
> The Ruby training with D. Black, G. Brown, J.McAnally
> Compleat Jan 22-23, 2010, Tampa, FL
> Rubyist http://www.thecompleatrubyist.com
>
> David A. Black/Ruby Power and Light, LLC (http://www.rubypal.com)

--
Posted via http://www.ruby-forum.com/.

James Edward Gray II

unread,
Nov 14, 2009, 3:28:35 PM11/14/09
to
On Nov 14, 2009, at 1:18 PM, Marnen Laibow-Koser wrote:

> James Edward Gray II wrote:
>> On Nov 13, 2009, at 10:27 PM, Marnen Laibow-Koser wrote:
>>
>>>
>>> Do you make much use of singleton mixins or singleton methods in your
>>> code? I know I don't.
>>
>> I have been doing a lot more of mixing modules into individual objects,
>> yes. I have been more than pleased with the results too. I think it's
>> something we should all try to do more of.
>
> How do you keep such code maintainable? It seems to me that
> circumventing the class system on a regular basis makes it harder to
> tell what's what.

Well, I've said that I don't consider the class system a definition of my types. This is why. :)

Honestly, I haven't had trouble with the approach yet. I can still do an is_a?() check for either the class or the mixed in modules, if I need to check what something can do.

I don't consider this circumventing Ruby's type system. Ruby's type system handles mixed in modules just fine. We just tend to think of the inheritance hierarchy as types. Ruby has a more open definition than that.

Perhaps I'll eventually run into a situation where I regret handling it by mixing modules into objects. I promise to admit it when I do. Until then though, all of my experiences with it have been very positive.

> Yes, I can see how this would be useful to modify singleton objects.
> But where a class has more than one or two instances, wouldn't this be
> extremely confusing?

More confusing than trying to figure out where some code reopened the class you are looking at and rewrote a method to change it's behavior? Not to me. At least Ruby will tell me about mixed in modules!

>> I think we should do more of this. For example, I think we could return
>> an Array that mixes in a Paginated module instead of a
>> PaginatedCollection object that inherits from Array. That feels more
>> right to me. It's an Array and it has some extra functionality added in
>> related to pagination. The uses go on and on.
>
> That feels hackish to me. If a common type like Array -- of which there
> could be hundreds of instances in a typical program -- needs extra
> functionality on some instances, it seems clearer and more
> intention-revealing to subclass it. Where's the benefit of the
> singleton mixin here?

Where's the benefit of subclassing? I thought it was pretty well accepted that subclassing is pretty tight coupling and can be pretty fragile. Many Design Patterns are about avoiding subclassing for just that reason, right?

If you subclass, you've already made some heavy decisions. You are saying pagination returns a special Array. What do you do when I want a paginated SortedSet (from Ruby's standard library)? You will be pretty much starting over, building another specialized subclass, I assume. I'll be thinking, "What changes does my Paginated module need to support SortedSet?" I would rather be in that position, that's all.

>>>> A class, which is what
>>>> people traditionally take for the type, is just one piece of an object's
>>>> identity.
>>>
>>> You're right. But with a proper class system, my point about not
>>> needing Apps Hungarian in Ruby still stands, I think. Do you disagree?
>>
>> I was agreeing with you, yes. I was saying that adding an a_ or s_ to
>> the beginning of a variable name, assumably to indicate Array or String,
>> is a damaging practice, because that's not necessarily all you need to
>> know about the object. I think it promotes the wrong kind of thinking
>> about Ruby's types.
>
> That's Systems Hungarian. Check out Joel's article if you haven't
> already.

Yes, I've read it. I'm still agreeing with you. :)

My examples, a and s for (I assume) Array and String, are the actual examples that started this discussion. That's what I feel should be discouraged and, yes, it's called Systems Hungarian.

I can see a little value in Apps Hungarian, but, again agreeing with you, I would rather build the protection into the code than the variable names. Or just teach the code to do the right thing, like converting to a common measurement and then combining values.

I do see that I quoted pretty badly above though, which is probably what made this so confusing. Sorry!

James Edward Gray II


James Edward Gray II

unread,
Nov 14, 2009, 3:36:03 PM11/14/09
to
On Nov 14, 2009, at 1:40 PM, Marnen Laibow-Koser wrote:

> But why is it worth extending an object instead of subclassing it?
>
> In other words,
> class SpecialString < String
> def method_I_need_once_or_twice
> ...
> end

> ...


> @some_string = SpecialString.new(@some_string)
>
> seems to me like the right way to do this -- you can check if a given
> String instance is a SpecialString or a plain String, or simply use
> overriding and polymorphism for delegation.

If you mix a module into the String, you can still use is_a?() to check for that module. It can also override methods. The object is still polymorphic. I'm not understanding the advantages you think we are losing.

> Yet, if I understand you correctly, you and James are claiming that it
> is preferable to do
> class << @some_string
> def method_I_need_once_or_twice
> end
>
> Do I understand correctly?

I would much prefer to mix a module into the object. It has all the advantages of changing a singleton class, but it participates in the type system, can be read by RDoc, etc.

> If so, why?

Hopefully I've made my case by now, but just to sum it up one more time: I think it's more flexible than subclassing, it does participate in the type system, it can be documented. I'm not seeing the minuses.

James Edward Gray II

Marnen Laibow-Koser

unread,
Nov 14, 2009, 4:22:49 PM11/14/09
to
James Edward Gray II wrote:
> On Nov 14, 2009, at 1:40 PM, Marnen Laibow-Koser wrote:
>
>> seems to me like the right way to do this -- you can check if a given
>> String instance is a SpecialString or a plain String, or simply use
>> overriding and polymorphism for delegation.
>
> If you mix a module into the String, you can still use is_a?() to check
> for that module. It can also override methods. The object is still
> polymorphic. I'm not understanding the advantages you think we are
> losing.
>
>> Yet, if I understand you correctly, you and James are claiming that it
>> is preferable to do
>> class << @some_string
>> def method_I_need_once_or_twice
>> end
>>
>> Do I understand correctly?
>
> I would much prefer to mix a module into the object. It has all the
> advantages of changing a singleton class, but it participates in the
> type system, can be read by RDoc, etc.

OK. That makes more sense to me than the sample I gave.

>
>> If so, why?
>
> Hopefully I've made my case by now, but just to sum it up one more time:
> I think it's more flexible than subclassing,

Even subclassing like
class SpecialString < String
include SpecialModule
end
?

It seems to me that this is the best of both worlds.

>it does participate in the
> type system,

True. I hadn't considered singleton module inclusion as completely as I
should have.

> it can be documented.

How can it be documented? In the example I gave above, the rdoc for
SpecialString will say that it includes SpecialModule. I don't see how
to do that with a singleton mixin.

> I'm not seeing the minuses.

Start with the lack of documentation.

Also, I admit that something feels deeply *wrong* to me about extending
individual objects (on more than an occasional basis) without changing
their nominal class. I think a class is still a useful construct, but
too much singleton manipulation renders it meaningless -- a class seems
to me like a rule for how an object will act, and singleton manipulation
creates too many exceptions to the rules. That feels like sloppy,
unstructured programming to me, although I'll be interested to try the
pattern in practice and see what comes of it.

>
> James Edward Gray II

Best,
--
Marnen Laibow-Koser
http://www.marnen.org
mar...@marnen.org

James Edward Gray II

unread,
Nov 14, 2009, 5:10:52 PM11/14/09
to
On Nov 14, 2009, at 3:22 PM, Marnen Laibow-Koser wrote:

> James Edward Gray II wrote:
>> Hopefully I've made my case by now, but just to sum it up one more time:
>> I think it's more flexible than subclassing,
>
> Even subclassing like
> class SpecialString < String
> include SpecialModule
> end
> ?
>
> It seems to me that this is the best of both worlds.

It seems to me like an extra step that isn't needed. :)

Obviously, this does work too, yes.

About the only downside I can think of now is that using this technique, I need to be in control of the code where the object is created. In other words, I need to make sure that code does SpecialString.new() instead of String.new() (or using a String literal). I guess I could still mix the module into an object after the fact, but that raises the excellent question of what is the class doing here?

>> it can be documented.
>
> How can it be documented? In the example I gave above, the rdoc for
> SpecialString will say that it includes SpecialModule. I don't see how
> to do that with a singleton mixin.

It fits into RDoc comments naturally:

# Returns an Array enhanced by the Paginated module.
def paginate(…)

end

# This module is used to add pagination support to objects…
module Paginated

end

>> I'm not seeing the minuses.
>
> Start with the lack of documentation.

OK, I answered that.

Now you answer the question you ignored, what about the well-known issue of inheritance producing tight coupling. :) Here's a link to a good description of the problem, from a Java point of view:

http://www.javaworld.com/javaworld/jw-08-2003/jw-0801-toolbox.html

James Edward Gray II


Marnen Laibow-Koser

unread,
Nov 14, 2009, 5:34:31 PM11/14/09
to
James Edward Gray II wrote:
> On Nov 14, 2009, at 3:22 PM, Marnen Laibow-Koser wrote:
>
>> It seems to me that this is the best of both worlds.
> It seems to me like an extra step that isn't needed. :)
>
> Obviously, this does work too, yes.
>
> About the only downside I can think of now is that using this technique,
> I need to be in control of the code where the object is created. In
> other words, I need to make sure that code does SpecialString.new()
> instead of String.new() (or using a String literal). I guess I could
> still mix the module into an object after the fact, but that raises the
> excellent question of what is the class doing here?

I think I can answer this question, but the perspective from which
you're asking is unclear.

>
>>> it can be documented.
>>
>> How can it be documented? In the example I gave above, the rdoc for
>> SpecialString will say that it includes SpecialModule. I don't see how
>> to do that with a singleton mixin.
>
> It fits into RDoc comments naturally:
>
> # Returns an Array enhanced by the Paginated module.
> def paginate(�)
> �
> end
>
> # This module is used to add pagination support to objects�
> module Paginated
> �
> end
>

Yes, of course. I was asking more about automatic documentation such as
RDoc picks up for included modules.

But I tend to be pretty conscientious about writing doc comments for
methods.

>>> I'm not seeing the minuses.
>>
>> Start with the lack of documentation.
>
> OK, I answered that.
>
> Now you answer the question you ignored, what about the well-known issue
> of inheritance producing tight coupling. :) Here's a link to a good
> description of the problem, from a Java point of view:
>
> http://www.javaworld.com/javaworld/jw-08-2003/jw-0801-toolbox.html
>

I'm not ignoring it. I'm just not really convinced that it's a problem.
In cases where inheritance is called for, I'm not sure tight coupling is
inappropriate. If tight coupling is inappropriate in a given case,
perhaps that's a sign that composition or module inclusion should be
used. If SpecialString is a decorated String, I don't see too much
reason for decoupling from String.

I will consider this further and read the article, though. I'm far from
certain that I'm right on this point.

Now, what about the sloppiness of creating too many exceptions? :)

Best,
--
Marnen Laibow-Koser
http://www.marnen.org
mar...@marnen.org

> James Edward Gray II

James Edward Gray II

unread,
Nov 14, 2009, 5:41:55 PM11/14/09
to
On Nov 14, 2009, at 4:34 PM, Marnen Laibow-Koser wrote:

> I'm far from certain that I'm right on this point.

I'm not sure I'm write either. It just seems to be working well for me so far.

James Edward Gray II

Marnen Laibow-Koser

unread,
Nov 14, 2009, 9:56:13 PM11/14/09
to
James Edward Gray II wrote:
[...]

> Now you answer the question you ignored, what about the well-known issue
> of inheritance producing tight coupling. :) Here's a link to a good
> description of the problem, from a Java point of view:
>
> http://www.javaworld.com/javaworld/jw-08-2003/jw-0801-toolbox.html

Oh, Allen Holub. This is the same guy who thinks that MVC is not
object-oriented (see http://c2.com/cgi/wiki?MvcIsNotObjectOriented for
discussion and link to original article). Anyway, Holub's argument --
that one should code to interfaces, not classes -- means a lot to
programming style in Java, but very little to programming style in Ruby,
I think, precisely because Ruby is dynamically typed. You don't have to
declare typed variables in Ruby, and you're generally not even
interested in the type of an object in Ruby, because Ruby's duck-typing
means you don't care.

You know all this, of course. But I don't think you appreciate that it
basically renders meaningless the "flexibility" part of Holub's thesis.
His examples are good in Java, but not in Ruby.

Consider the Ruby version of Holub's first "bad" Java example:
def f
@list = LinkedList.new
g(@list)
end

def g(list)
list.add(...)
g2(list)
end

Unlike in Java, g doesn't need to assume anything about its argument
except that it won't complain when it receives an :add message. We
could change the type of @list completely and g would not break in Ruby,
as long as the new object can handle an :add message.

Likewise, Holub's first alleged example of the coupling problem is
nothing of the kind. The problem with his Stack class isn't that it
uses implementation inheritance -- it's that it uses implementation
inheritance *inappropriately*, by exposing a public method in the base
class that will leave the derived class in an inconsistent state.
That's just a plain bug. In this case, composition should have been
used instead of inheritance (yes, I wrote that before reading that it
was Holub's suggested solution too!). It's like using a hammer to drive
a screw -- the fact that it doesn't work very well means that it's the
*wrong* tool, not a *generally bad* tool.

The Monitorable_stack example is certainly a better example of the
fragile base class problem. But I don't think it's fixable with
interface inheritance. Apparently, neither does Holub: his supposedly
"interface-based" rewrite is actually composition-based (notice the
Monitorable_stack.stack member), and would work just as well if no
interfaces were involved. I will be charitable and assume that Holub
was merely being sloppy here, not deliberately intellectually dishonest.
Either way, it does nothing to advance his argument in favor of
interfaces.

This article is appallingly poorly thought out, and in any case only
warns against using typed arguments (hardly possible anyway in Ruby) and
using inheritance where composition was actually necessary (a basic OO
analysis mistake). It brings little or nothing useful to the present
discussion.

Phrogz

unread,
Nov 14, 2009, 11:16:25 PM11/14/09
to
On Nov 13, 9:21 pm, James Edward Gray II <ja...@graysoftinc.com>
wrote:

> I would like to see us move away from considering classes to be types at all in Ruby.
> Who knows what modules an object has mixed into it and who knows what singleton
> methods are defined on it.  A class, which is what people traditionally take for the
> type, is just one piece of an object's identity.

Ill-formed thoughts on this:
a) Duck-Typing FTW
b) Even Google buys duck-typing, albeit in a more formalized way:
http://golang.org/doc/go_lang_faq.html#inheritance

James Edward Gray II

unread,
Nov 15, 2009, 12:45:48 AM11/15/09
to
On Nov 14, 2009, at 8:56 PM, Marnen Laibow-Koser wrote:

> This article is appallingly poorly thought out, and in any case only
> warns against using typed arguments (hardly possible anyway in Ruby) and
> using inheritance where composition was actually necessary (a basic OO
> analysis mistake). It brings little or nothing useful to the present
> discussion.

I chose an example of the discussion. It is in no way isolated. The Gang of Four book is largely about avoiding inheritance. James Gosling admits that he wishes he could do away with objects for typing in Java, as the article says. You even stated that types aren't all that important in Ruby, which I agree with, but that seems like and argument against all of your examples that handle all typing with subclassing.

Anyway, I've given examples and provided reference points. I hope I've made my case. Now we're probably just bugging people, so I'm going to let it go.

James Edward Gray II

Marnen Laibow-Koser

unread,
Nov 15, 2009, 2:19:57 AM11/15/09
to
James Edward Gray II wrote:
> On Nov 14, 2009, at 8:56 PM, Marnen Laibow-Koser wrote:
>
>> This article is appallingly poorly thought out, and in any case only
>> warns against using typed arguments (hardly possible anyway in Ruby) and
>> using inheritance where composition was actually necessary (a basic OO
>> analysis mistake). It brings little or nothing useful to the present
>> discussion.
>
> I chose an example of the discussion.

Unfortunately, it's a terrible example. A poorly reasoned, marginally
relevant article from an untrustworthy source is not really going to
convince anyone (at least, it shouldn't). I have been trying to read
Rick's article, but it always seems to take forever to download.

> It is in no way isolated. The
> Gang of Four book is largely about avoiding inheritance.

Really? My memory from when I read the GoF book is that many of the
patterns (certainly not all) use inheritance as a basic building block.

A check through the GoF pattern pages on Wikipedia, however, shows that
I may be mistaken. Many of the cases I had remembered as inheritance
are actually implementation of an interface, and most of the others are
inheritance from abstract classes (which are sort of another kind of
interface).

Then again, while I appreciate the concepts inherent in the GoF
patterns, I don't find those 23 patterns popping up all that much in the
code I write. I wonder why. (For the most part, I don't deliberately
try to code or refactor to GoF patterns, but if they want to emerge from
refactoring, I certainly won't stand in their way.)

> James Gosling
> admits that he wishes he could do away with objects for typing in Java,
> as the article says. You even stated that types aren't all that
> important in Ruby, which I agree with, but that seems like and argument
> against all of your examples that handle all typing with subclassing.

Perhaps so. I like Ruby's duck-typing, but I don't seem to be willing
to use it as a replacement for conventional inheritance. I seem to
believe that it works best as a complement to a traditional type
hierarchy. But these are semi-conscious attitudes.

Maybe that means I don't know how to use duck typing properly. I have
to think about this some more.

>
> Anyway, I've given examples and provided reference points. I hope I've
> made my case. Now we're probably just bugging people, so I'm going to
> let it go.

What's to let go? I don't think we're off topic for the list...

David A. Black

unread,
Nov 15, 2009, 7:50:53 AM11/15/09
to
Hi --

> ...


> @some_string = SpecialString.new(@some_string)
>
> seems to me like the right way to do this -- you can check if a given
> String instance is a SpecialString or a plain String, or simply use
> overriding and polymorphism for delegation.
>
> Yet, if I understand you correctly, you and James are claiming that it
> is preferable to do
> class << @some_string
> def method_I_need_once_or_twice
> end
>
> Do I understand correctly? If so, why? I hesitate to contradict such
> experts as you and James, but I'm really not seeing the benefit. For
> one thing, type-checking completely falls down with this pattern.
> Polymorphism may or may not, depending on how it's implemented. And I
> don't see a single advantage that we get in return for the loss of
> type-checking. What am I missing?

I'm not saying (and I don't think James would say) that this is a
"winner-take-all" situation where we all have to choose one technique
and do only that. But I think extending core objects is a very good
way to add functionality to them.

It sounds like by type-checking you mean class-checking (or
ancestry-checking). It's kind of a circular argument, in the sense
that if you decide that it's important to know an object's class
because you're relying on that to tell you exactly what the object
does, then it's bad if the object does other stuff (i.e., if its type
diverges from its class).

It's certainly possible to look at things that way, but it seems to me
that it means you're fighting Ruby. The entire thrust of Ruby's object
model is to put the focus on the objects rather than their classes.
(Yes, I know that classes are objects :-) Them too -- which is why
class methods are manifested as singleton methods on class objects,
rather than some completely separate language-level construct.)

Here's one of my favorite historical observations about Ruby, where
ruby10 is the Ruby 1.0 interpreter:

[dblack@ruby-versions ~]$ ruby10 -e 'puts "hi"'-e:1: NameError:
undefined method `puts' for main(Object)
[dblack@ruby-versions ~]$ ruby10 -e 'a = "hi"; class << a; def talk;
print self + "!\n"; end; end; a.talk'
hi!

In other words, Ruby 1.0 had no puts statement... but it *did* have
singleton classes. That's a great corrective to the perception I think
some people have that singleton behaviors are some kind of
meta-wizard-add-on complication to the language. They're not; the
language was designed from the beginning to make it possible to
engineer objects on an individual basis.

I've heard people say that, while that's true, it's undisciplined to
actually use any of these techniques. I disagree radically. Of course,
I don't spend all that much of my time as a Ruby programmer writing
singleton methods -- but I do regard the individual object as the
center of gravity, and I regard classes largely as a convenience-macro
for creating objects bundled with certain behaviors at their birth
that may or may not represent what they do during their lifetimes.

This is also why, in my book and in my training, I present singleton
methods *first*, and classes second. (And I completely understand why
Matt Neuberg introduces modules before classes[1] -- which I usually
don't but it's an extremely intriguing idea.)

Classes exist in Ruby, and no one is awarding points based on how many
singleton methods one writes. But for me, anyway, the class part of
the model floats on top of the real business, which is the objects. So
I don't like to commit myself to having things break just because
objects aren't aligned perfectly type-to-class.

(There's more to say but that's long enough for now :-)


David

[1] http://www.apeth.com/rbappscript/02justenoughruby.html
--
THE COMPLEAT RUBYIST, Ruby training with Black/Brown/McAnally!
January 22-23, Tampa, Florica
Info and registration at http://www.thecompleatrubyist.com
--------------------------------------
My new job: http://tinyurl.com/yfpn9hz

Marnen Laibow-Koser

unread,
Nov 15, 2009, 8:30:03 AM11/15/09
to
David A. Black wrote:
> Hi --
>
> On Sun, 15 Nov 2009, Marnen Laibow-Koser wrote:
>
>>>
>>
>> In other words,

>>
>> don't see a single advantage that we get in return for the loss of
>> type-checking. What am I missing?
>
> I'm not saying (and I don't think James would say) that this is a
> "winner-take-all" situation where we all have to choose one technique
> and do only that. But I think extending core objects is a very good
> way to add functionality to them.
>
> It sounds like by type-checking you mean class-checking (or
> ancestry-checking). It's kind of a circular argument, in the sense
> that if you decide that it's important to know an object's class
> because you're relying on that to tell you exactly what the object
> does, then it's bad if the object does other stuff (i.e., if its type
> diverges from its class).

Yes, I suppose it's somewhat circular. If the truth be told, I don't do
a heck of a lot of class checking in Ruby, and I had forgotten that
is_a? checks included modules, which goes a long way toward overcoming
my pragmatic objection.

>
> It's certainly possible to look at things that way, but it seems to me
> that it means you're fighting Ruby. The entire thrust of Ruby's object
> model is to put the focus on the objects rather than their classes.
> (Yes, I know that classes are objects :-) Them too -- which is why
> class methods are manifested as singleton methods on class objects,
> rather than some completely separate language-level construct.)

Interesting point. Perhaps I misunderstood the language.

>
> Here's one of my favorite historical observations about Ruby, where
> ruby10 is the Ruby 1.0 interpreter:
>
> [dblack@ruby-versions ~]$ ruby10 -e 'puts "hi"'-e:1: NameError:
> undefined method `puts' for main(Object)
> [dblack@ruby-versions ~]$ ruby10 -e 'a = "hi"; class << a; def talk;
> print self + "!\n"; end; end; a.talk'
> hi!
>
> In other words, Ruby 1.0 had no puts statement... but it *did* have
> singleton classes. That's a great corrective to the perception I think
> some people have that singleton behaviors are some kind of
> meta-wizard-add-on complication to the language.

I don't think I had ever thought that.

> They're not; the
> language was designed from the beginning to make it possible to
> engineer objects on an individual basis.

I figured that.

>
> I've heard people say that, while that's true, it's undisciplined to
> actually use any of these techniques. I disagree radically. Of course,
> I don't spend all that much of my time as a Ruby programmer writing
> singleton methods -- but I do regard the individual object as the
> center of gravity, and I regard classes largely as a convenience-macro
> for creating objects bundled with certain behaviors at their birth
> that may or may not represent what they do during their lifetimes.

In other words, you're advocating writing Ruby rather like JavaScript.
That makes some sense -- although, as a devil's advocate, I could ask
why, in that case, Matz didn't design the language with a prototype
model. :)

>
> This is also why, in my book and in my training, I present singleton
> methods *first*, and classes second. (And I completely understand why
> Matt Neuberg introduces modules before classes[1] -- which I usually
> don't but it's an extremely intriguing idea.)

Do you worry that this will cause people to excessively avoid classes in
favor of singleton mixins? Or is that the idea?

>
> Classes exist in Ruby, and no one is awarding points based on how many
> singleton methods one writes. But for me, anyway, the class part of
> the model floats on top of the real business, which is the objects.

That's an interesting point of view. I was about to ask why you believe
that, but then I realized that the fact that Ruby allows so much
singleton manipulation kind of supports that point.

> So
> I don't like to commit myself to having things break just because
> objects aren't aligned perfectly type-to-class.
>

Nor do I. I certainly don't write code that will do that in the first
place...

> (There's more to say but that's long enough for now :-)
>
>
> David

Best,

Eleanor McHugh

unread,
Nov 15, 2009, 4:53:43 PM11/15/09
to
On 15 Nov 2009, at 13:30, Marnen Laibow-Koser wrote:
> David A. Black wrote:
>>
>> I've heard people say that, while that's true, it's undisciplined to
>> actually use any of these techniques. I disagree radically. Of
>> course,
>> I don't spend all that much of my time as a Ruby programmer writing
>> singleton methods -- but I do regard the individual object as the
>> center of gravity, and I regard classes largely as a convenience-
>> macro
>> for creating objects bundled with certain behaviors at their birth
>> that may or may not represent what they do during their lifetimes.
>
> In other words, you're advocating writing Ruby rather like JavaScript.
> That makes some sense -- although, as a devil's advocate, I could ask
> why, in that case, Matz didn't design the language with a prototype
> model. :)

RubyQuiz #214 involved implementing Ruby for prototype inheritance. Of
the four solutions provided the longest is Matthias Reitinger's which
is just 33 lines of code, so prototype inheritance is hardly a complex
addition to the language :)

I think the main point from this whole discussion is that Ruby is an
object-oriented language, not a class-oriented or a type-oriented one.
Duck typing can feel a little transgressive at first because we've
been brainwashed for decades that programming is all about
categorisation and type-safety, a mathematical endeavour. But nearly
all real-world problems display a messiness and inexactitude which
either make the use of type as the core criterion of design fragile
and/or overly verbose: fragile because it isn't always obvious where
the boundaries of a given type are drawn, and verbose because an
apparently simple composite type may involve the definition of many
hundreds of more specialised types and the coordination of their
interactions.

Duck typing works well for the very same reason that when I read a
book and it has a sentence such as "John kicks the ball" the only
information I need about John is whether or not he can kick, and the
only information I need about the ball is how it responds to being
kicked. Now there's still a lot of information contained in this
scenario but the laziness of the evaluation means that most of the
supposed value that type can bring to bear on the problem can be
achieved with much less effort.

Or put another way, mathematics is rarely a good user interface for
design even though it's an excellent one for analysis and decomposition.


Ellie

Eleanor McHugh
Games With Brains
http://slides.games-with-brains.net
----
raise ArgumentError unless @reality.responds_to? :reason


0 new messages