Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Ruby > The duck's backside

Reply
Thread Tools

The duck's backside

 
 
Eleanor McHugh
Guest
Posts: n/a
 
      05-28-2008
On 28 May 2008, at 18:57, Mark Wilden wrote:
> On May 28, 2008, at 8:24 AM, Eleanor McHugh wrote:
>> doit a, b, c = nil
>> raise ArgumentError, "needs exactly one additional argument" if b
>> && c
>> v = b || c
>> raise ArgumentError, "need a numeric parameter" unless
>> v.respond_to?(:to_int)
>> @post "first=#{a}&" + (b.nil? ? "second=ignored&third=#{v}" :
>> "second=#{v}&third=ignored")
>> end

>
> This looks like a great solution!
>
> But if you want to check for numeric parameters, would it be better
> just to use Numeric === v ? I know this isn't the ducky way, but it
> certainly seems more "intentional."


That'd be marginally more readable but to my mind it's overly
cautious: what's the point of using Ruby if you're still going to
follow static typing conventions?

Ellie

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



 
Reply With Quote
 
 
 
 
Mark Wilden
Guest
Posts: n/a
 
      05-28-2008
On May 28, 2008, at 11:45 AM, Eleanor McHugh wrote:

>> But if you want to check for numeric parameters, would it be better
>> just to use Numeric === v ? I know this isn't the ducky way, but it
>> certainly seems more "intentional."

>
> That'd be marginally more readable but to my mind it's overly
> cautious: what's the point of using Ruby if you're still going to
> follow static typing conventions?


The only point of using Ruby (or any other tool in the universe) is to
get a job done. I would never use a Ruby feature just because Ruby
makes it possible.

Numeric === v is not a static typing convention (see http://en.wikipedia.org/wiki/Type_system)
It's just OOP. It says in computer-understandable terms exactly what
you expressed in English: "v is numeric."

I don't even know what the ability to respond to :to_int means. Does
that mean the entity is numeric? Does a numeric string qualify? Does a
floating point number qualify? Could Array support this method? Does
every class that responds to to_int do semantically equivalent things?
(think Cowboy#draw and Artist#draw). The only way to tell is to test
it or look it up. This doesn't make it "marginally" less readable than
Numeric === v, IMO.

Different strokes, of course...

///ark

 
Reply With Quote
 
 
 
 
David Masover
Guest
Posts: n/a
 
      05-28-2008
On Wednesday 28 May 2008 15:09:23 Mark Wilden wrote:
> On May 28, 2008, at 11:45 AM, Eleanor McHugh wrote:


> > That'd be marginally more readable but to my mind it's overly
> > cautious: what's the point of using Ruby if you're still going to
> > follow static typing conventions?

>
> The only point of using Ruby (or any other tool in the universe) is to
> get a job done. I would never use a Ruby feature just because Ruby
> makes it possible.


Put it this way: What's the point of using, say, Erlang, if you never use its
concurrency features?

Granted, Ruby has a bit more going for it, but a lot of the joy of using Ruby
is how much less work you end up having to do when you don't have to think
about types too much.

> . It's just OOP. It says in computer-understandable terms exactly what
> you expressed in English: "v is numeric."


No, it says "v is of class Numeric." It's very explicit, and makes the
assumption that anything which acts like a number will eventually inherit
from Numeric.

> I don't even know what the ability to respond to :to_int means.


I think it would be :to_i, actually... And I'd probably just call foo.to_i,
rather than testing for the existence of to_i.

Another possibility is: v.integer?

> Does
> that mean the entity is numeric?


In a sense.

> Does a numeric string qualify?


Yes, it'll be parsed.

> Does a
> floating point number qualify?


Yes, it'll be rounded down.

> Could Array support this method?


Anything could. Open classes mean you could add one to Array. But out of the
box, it doesn't appear to.

> Does
> every class that responds to to_int do semantically equivalent things?


Probably -- to_i is very much baked into the language, along with to_s and
friends.

Remember the above -- I could actually completely redefine Array, or Numeric,
etc. So even your assumption that "Numeric === foo" tests for Numeric is
really only based on convention -- you're assuming that no one, anywhere in
your code, is doing stuff like this:

Numeric = nil

> (think Cowboy#draw and Artist#draw).


Yes, that is a downside of duck typing, as currently implemented, but doesn't
really apply to :to_i.

Also, context matters. If Cowboy and Artist are both in a GUI widget library,
that Cowboy is asking for trouble. And it's worth asking how the Cowboy came
to be in an art studio, or how the Artist came to be in a saloon -- there are
likely going to be more elegant solutions than

if Cowboy === bill
...

 
Reply With Quote
 
John Carter
Guest
Posts: n/a
 
      05-28-2008
On Thu, 29 May 2008, Jim Menard wrote:

>> What is the Duck way to check arguments? I have to because I'm passing
>> on to a rather fragile web service.

>
> Don't bother. Use your arguments, assuming they are the correct type.
> If they are not, then a runtime exception will be thrown.


Yes and No.

If the bug is on the backtrace, this works fine. An exception is
thrown, the backtrace is printed, and you inspect each one and you
find the wrong code, and fix it.

End of story.

If however you are doing, as is quite often done, constructing an
object, passing in some of the instance variables as parameters to the
constructor...

The use that triggers the exception, is quite likely _not_ to have the
buggy line on the backtrace. The buggy line was on the call graph
heading towards the constructor, not the use of that instance.

The duck's backside is not watertight, the duck sinks.

Hence my static_type_check set of utilities...

So in...

class MyObject

def initialize( _a, _b, _c)
@a = _a.quacks_like( :qwaak, :waddle, :swim)
@b = _b.static_type_check String
@c = _c.polymorphic_type_check MyBaseClass
end

# Without the checks above, the exceptions would be thrown here, with the buggyline nowhere on the backtrace!
def my_use
@a.qwaak( @b) + @c
end
end

# Abstract base class for all the type check exceptions
class TypeCheckException < Exception
end

# This exception is thrown in event of a method being invoked with an
# object of the wrong duck type.
class DuckTypingException < TypeCheckException
end

# This exception is thrown in event of a method being invoked with a
# parameter of of the wrong static type.
class StaticTypeException < TypeCheckException
end

# This exception is thrown in event of a method being invoked with a
# parameter of of the wrong polymorphic type.
class PolymorphicTypeException < TypeCheckException
end

class Object

# Raise a DuckTypingException unless the object responds to all symbol.
def quacks_like( *symbols)
symbols.each do |symbol|
raise DuckTypingException, "Duck typing error, expected this object to respond to :#{symbol}, but found class #{self.class}\n\t\t#{symbol.inspect}" unless
respond_to? symbol
end
end

def static_type_check_boolean
raise StaticTypeException, "Static type check error, expected object to be a boolean, found '#{self.class}'
\t\t#{self.inspect}" unless
(self.class == TrueClass) ||
(self.class == FalseClass) ||
(self.class == NilClass)
self
end


def static_type_check( klass)
raise StaticTypeException, "Static type check error, expected object to be exactly class '#{klass}', found '#{self.class}'\n\t\t#{self.inspect}" unless
self.class == klass
self
end

def static_type_check_each( klass)
each do |object|
object.static_type_check klass
end
self
end

def polymorphic_type_check_each( klass)
each do |object|
object.polymorphic_type_check klass
end
self
end

def polymorphic_type_check( klass)
# @@static_caller[caller[1]]+=1
raise PolymorphicTypeException, "Polymorphic type check error, expected object to be a kind of '#{klass}', found '#{self.class}'\n\t\t#{self.inspect}" unless
self.kind_of? klass
self
end

end



John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email : http://www.velocityreviews.com/forums/(E-Mail Removed)
New Zealand


 
Reply With Quote
 
David Masover
Guest
Posts: n/a
 
      05-28-2008
On Wednesday 28 May 2008 16:25:15 John Carter wrote:

> class MyObject
>
> def initialize( _a, _b, _c)
> @a = _a.quacks_like( :qwaak, :waddle, :swim)
> @b = _b.static_type_check String
> @c = _c.polymorphic_type_check MyBaseClass
> end
>
> # Without the checks above, the exceptions would be thrown here, with the

buggyline nowhere on the backtrace!
> def my_use
> @a.qwaak( @b) + @c
> end
> end


I'm fine with that, because 99% of the time, I'll never hit that exception.
When I do, it's usually possible to deal with it -- in this case, you would
find where @a is set, and add a check there.

I think you're arguing to add that check first. I would call that premature
strictness -- but if you have to do it, I like your way.

Oh, also: Most of the time, I use setters/getters (attr_accessor), even when
something's mostly going to be internal. That simplifies this process -- I
know there was a problem with a, so I can override a's setter with something
that performs the same check. But that's also probably overkill -- if a class
can't fit on a page, it's probably too big and should be broken down.

 
Reply With Quote
 
David Masover
Guest
Posts: n/a
 
      05-28-2008
On Wednesday 28 May 2008 16:25:15 John Carter wrote:
> # Raise a DuckTypingException unless the object responds to all symbol.
> def quacks_like( *symbols)
> symbols.each do |symbol|
> raise DuckTypingException, "Duck typing error, expected this

object to respond to :#{symbol}, but found class
#{self.class}\n\t\t#{symbol.inspect}" unless
> respond_to? symbol
> end
> end


Just occurred to me that this does have one flaw: It cannot check for what
happens when method_missing is hit. I doubt that's much of a problem in
practice, though.

 
Reply With Quote
 
Mark Wilden
Guest
Posts: n/a
 
      05-28-2008
On May 28, 2008, at 1:44 PM, David Masover wrote:

>> The only point of using Ruby (or any other tool in the universe) is
>> to
>> get a job done. I would never use a Ruby feature just because Ruby
>> makes it possible.

>
> Put it this way: What's the point of using, say, Erlang, if you
> never use its
> concurrency features?


All I can do is repeat what I said: the ONLY point to ANYthing (in
programming) is as a means to accomplish some purpose. If using Erlang
accomplishes a given purpose best, then it should be used, no matter
what its concurrency capabilities. One reason might simply be that a
given programmer is more comfortable in that language than another,
and can therefore accomplish the task faster.

> Granted, Ruby has a bit more going for it, but a lot of the joy of
> using Ruby
> is how much less work you end up having to do when you don't have to
> think
> about types too much.


I think you mean "classes," not "types." At any rate, I don't see how
Numeric === v involves more work than the alternative, so I don't
think that criterion applies here.

> No, it says "v is of class Numeric." It's very explicit, and makes the
> assumption that anything which acts like a number will eventually
> inherit
> from Numeric.


Well, the desired goal is in fact to recognize objects that are
numeric. One of the purposes of classes in OOP is to categorize things.

>> I don't even know what the ability to respond to :to_int means.

>
> I think it would be :to_i, actually... And I'd probably just call
> foo.to_i,
> rather than testing for the existence of to_i.


:to_i is actually rather different, as it will convert a string (even
a non-numeric string like "2a"). That's an example of having to know
the implementation of the method to determine whether testing
responds_to? makes sense. For all I know, :to_i may in fact be the
desired method, but Elizabeth used :to_int, and I assume it was for a
reason.

> Remember the above -- I could actually completely redefine Array, or
> Numeric,
> etc. So even your assumption that "Numeric === foo" tests for
> Numeric is
> really only based on convention -- you're assuming that no one,
> anywhere in
> your code, is doing stuff like this:
>
> Numeric = nil


That's not merely being unconventional--that's insanity. Anyway, it
applies even more so to methods, which aren't in the global namespace.

>> (think Cowboy#draw and Artist#draw).

>
> Yes, that is a downside of duck typing, as currently implemented,
> but doesn't
> really apply to :to_i.
>
> Also, context matters. If Cowboy and Artist are both in a GUI widget
> library,
> that Cowboy is asking for trouble.


That's true, but it wasn't my point. The question is whether all
methods with the same name in all active classes should be
semantically equivalent. I think that's a rather larger assumption
than that Numeric means "numeric."

///ark

 
Reply With Quote
 
John Carter
Guest
Posts: n/a
 
      05-29-2008
On Thu, 29 May 2008, David Masover wrote:

> I'm fine with that, because 99% of the time, I'll never hit that exception.
> When I do, it's usually possible to deal with it -- in this case, you would
> find where @a is set, and add a check there.
>
> I think you're arguing to add that check first. I would call that premature
> strictness -- but if you have to do it, I like your way.


Depends on what I'm doing. If I'm just doing bog standard coding, I'll
probably skip the checks.

If I'm doing something wildly experimental, or ripping up and
restructuring a large chunk of code, I'll probably splash checks
around with a heavy hand.

Gives me a quick heads up that I'm being stupid. Forces me to think a
little, "what do I really want coming it here? I Dunno really, but it
must quack like this. "

Run the unit test, ooh looky, I'm getting something that doesn't quack
like that. No, no surprise now I think about it, but it holds, has, or
makes a thing that quacks like that. A quick fix in the editor and off
I go again.




John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email : (E-Mail Removed)
New Zealand


 
Reply With Quote
 
David Masover
Guest
Posts: n/a
 
      05-29-2008
On Wednesday 28 May 2008 17:23:30 Mark Wilden wrote:
> On May 28, 2008, at 1:44 PM, David Masover wrote:
>
> > Put it this way: What's the point of using, say, Erlang, if you
> > never use its
> > concurrency features?

>
> All I can do is repeat what I said: the ONLY point to ANYthing (in
> programming) is as a means to accomplish some purpose. If using Erlang
> accomplishes a given purpose best, then it should be used, no matter
> what its concurrency capabilities. One reason might simply be that a
> given programmer is more comfortable in that language than another,
> and can therefore accomplish the task faster.


Even if the problem doesn't require concurrency, the main reason for choosing
Erlang in the first place is its threading model. If you don't like Erlang's
threading, chances are, the rest of it is done better in other languages.

I'm not saying that we don't want you if you won't do Ruby duck typing. Just
saying that I consider duck typing to be a major draw to Ruby in the first
place.

I kind of feel like you're doing the equivalent of this:

i = 0
while(i < some_array.length)
do_something_with(some_array[i])
i += 1
end

Yes, Ruby can do that, but I think most of us agree that a major appeal of
Ruby is being able to do this instead:

some_array.each { |item|
do_something_with item
}

> > No, it says "v is of class Numeric." It's very explicit, and makes the
> > assumption that anything which acts like a number will eventually
> > inherit
> > from Numeric.

>
> Well, the desired goal is in fact to recognize objects that are
> numeric. One of the purposes of classes in OOP is to categorize things.


Given single inheritance, you're not going to force everything into the exact
category it belongs. I know I implemented a class to represent DNS serial
numbers and their associated math. It didn't inherit from Numeric, but it did
have to_i.

Maybe that was bad design on my part, but the point of duck typing is that we
don't need to care if it calls itself "Numeric". Instead, we care that it
acts like a Numeric -- it responds_to +, -, and probably to_i and integer?

You've probably heard all this before, of course.

> > Remember the above -- I could actually completely redefine Array, or
> > Numeric,
> > etc. So even your assumption that "Numeric === foo" tests for
> > Numeric is
> > really only based on convention -- you're assuming that no one,
> > anywhere in
> > your code, is doing stuff like this:
> >
> > Numeric = nil

>
> That's not merely being unconventional--that's insanity. Anyway, it
> applies even more so to methods, which aren't in the global namespace.


Alright, without altering the global namespace, and with very possibly a good
excuse, I could do something like this:

class IpAddress < Integer
def to_s
# return '12.34.56.78'
end
end

Now, that actually won't work without a bit of massaging -- the numeric
classes don't have constructors. And there's already a built in IP address
class, so this would be pointless.

But I'm not sure there's any more reason to believe that something which is a
Numeric (or claims to be) is going to give you the semantics you want, than
to believe the same of something which supports to_i (or to_int, which is
probably more correct).

> >> (think Cowboy#draw and Artist#draw).

> >
> > Yes, that is a downside of duck typing, as currently implemented,
> > but doesn't
> > really apply to :to_i.
> >
> > Also, context matters. If Cowboy and Artist are both in a GUI widget
> > library,
> > that Cowboy is asking for trouble.

>
> That's true, but it wasn't my point. The question is whether all
> methods with the same name in all active classes should be
> semantically equivalent. I think that's a rather larger assumption
> than that Numeric means "numeric."


The assumption you're making with Numeric isn't that Numeric means "numeric",
but that all possibly numeric values are contained in Numeric.

I think your use case had something to do with a fragile web service, so this
actually could make a lot of sense to you -- it might even be worth checking
if it's an Integer.

But in the general case, I like that much of my code is actually abstract
enough to swap something of an entirely different type (or class) in and have
it do something useful. Recently, I actually wrote an entire class for
storing things (a two-dimensional map) without knowing what kind of object
I'd be filling it with. I ended up filling it with Symbols.

 
Reply With Quote
 
Mark Wilden
Guest
Posts: n/a
 
      05-29-2008
On May 28, 2008, at 6:25 PM, David Masover wrote:

> I think your use case had something to do with a fragile web
> service, so this
> actually could make a lot of sense to you -- it might even be worth
> checking
> if it's an Integer.


Wasn't my use case.

///ark

 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off




Advertisments