How to react on nil or wrong object-type as parameter

On 8/7/06, Yochen G. [email protected] wrote:

So if I do understand you all correctly, in Ruby you put the
responsible to the caller of a method (eg print_salary()). If you want
to test that method, you test it only with the proper types.

Where type may not be related to the Object’s class or inheritance tree.
An object’s type for any method call is explicitly related to whether it
responds to the method we’re looking for with the arguments we provide
and the sort of return value we’re wanting. It’s significantly different
than Java in this way.

The next thing would be testing the method which includes the call of
print_salary() if it calls this methods with the wrong types of
parameter. Is this right?

Um, not really as I understand it.

One problem in this concept is that if some operations might work on a
parameter-object and the following is not the exception is thrown when
the object is already modified.

There are problems with your example code:

class Bank
@invitations_to_golf

This won’t do what you expect. Try:

def handle_client_well client
client.decrease_acoount(200)
client.check_portfolio
@invitations_to_golf.push client
end

end

class Bank
def initialize
@invitations_to_golf = []
end

def issue_golf_invitations(title, sender, message, date)
  @invitations_to_golf.each { |client|
    client.send_message title, sender, message, date
  }
end

def treat_client_well(client)
  if client.check_portfolio
    client.decrease_account(200)
    @invitations_to_golf << client
  end
end

end

What this does is that it requires that all clients have a
#check_portfolio method. If the Bank’s clients aren’t guaranteed to have
that, you’ll blow up here, as you’ve suggested. But how do you deal with
that? Well, you don’t look for a VeryRichClient. You do one of two
things:

class Client
def check_portfolio
nil
end
end

class LowBudgetClient < Client
end

class VeryRichClient < Client
def check_portfolio
true
end
end

So you modify the object you’re dealing with and expect to pass in so
that it always has a check_porfolio (that’s why I put the “if” test,
which you’d want anyway) or you do:

class Bank
def treat_client_well(client)
if client.respond_to?(:check_portfolio) and client.check_portfolio
client.decrease_account(200)
@invitations_to_golf << client
end
end
end

Or you handle your call a bit better:

jon_doe = LowBudgetClient.new
big_boss = VeryRichClient.new
big_money = Bank.new

big_money.handle_client_well big_boss
big_money.handle_client_well jon_doe rescue nil

One need not be explicit about how you handle a lot of things in Ruby.
Strictly speaking, you’d never want to have two classes for clients in
this way, but you may have things in the contents of the clients which
allow you to do things smarter.

-austin

Eric H. wrote:

  puts str.reverse

argument. That’s type checking. If you want to convert any object
received to a string, then we’re talking type conversion, which I
think should be left to the caller in many cases. But that’s just my
humble opinion, of course.

#to_str is not for type conversion. It exists for classes that duck
type completely to String and is called when a C string is needed by a
method in String to improve performance. In other words, #to_str should
only be implemented on objects that are already “Strings”.

Exactly my point. I think we’re misunderstanding each other here; if you
have an object that is a representation of a string, say

Bad example, but hey…

class Name
attr_accessor :first, :last

 def initialize(first, last)
   @first, @last = first, last
 end

end

Then it’s okay for it to have #to_str, e.g.

 def to_str
   "#{first} #{second}"
 end

My own logic is this: if an object is a natural string, but with more
information, then it can have #to_str. Otherwise, stick to #to_s.

A much better example is the one brought up on the list not long ago:

class RomanNumeral
def initialize(num)
@num = num.to_int
end

 # this really is an integer
 def to_int
   @num
 end

 # but there's not much sense in manipulating
 # it as a string
 def to_s
   ...
 end

end

If it were for a real application, I’d just implement an
Integer#to_roman method, but I still think this is a good example.

#to_s is for type conversion. Trust your users to provide a meaningful
#to_s. Don’t force them to inappropriately implement #to_str.

I agree wholeheartedly. I’m just bad at writing.

On Aug 8, 2006, at 2:45 AM, Daniel S. wrote:

  puts str + str # or `str * 2'

really a type restriction. We’re trying to solve two different

end

Then it’s okay for it to have #to_str, e.g.

def to_str
  "#{first} #{second}"
end

My own logic is this: if an object is a natural string, but with
more information, then it can have #to_str. Otherwise, stick to
#to_s.

A Name is not a String. You don’t #each it, you don’t #chop it, its
never #empty?, …


Eric H. - [email protected] - http://blog.segment7.net
This implementation is HODEL-HASH-9600 compliant

http://trackmap.robotcoop.com

On Aug 8, 2006, at 10:35 AM, Daniel S. wrote:

  str = obj.to_str

I favor using #to_str when I want to treat an object like a
needed by a method in String to improve performance. In other
def initialize(first, last)
My own logic is this: if an object is a natural string, but with
more information, then it can have #to_str. Otherwise, stick to
#to_s.
A Name is not a String. You don’t #each it, you don’t #chop it,
its never #empty?, …

That’s why you use the return value of #to_str.

You don’t call #to_str, String calls #to_str when it needs to.

If only String had #to_str, then why have it in the first place?
Why not just check the class?

It would be appropriate to define #to_str for objects that really are
Strings, but don’t inherit from String. A good example would be when
writing a bridge to another language, like Java strings or C# strings.

I’m not saying this is how everybody should do it, I’m just saying
that it’s a very smart way to handle types – duck typing being a
very similar way.

It isn’t a smart way to handle types, it is a broken and wrong way to
handle types.

Don’t confuse “duck types to String” with “has a String
representation (#to_s)”. I doubt you will ever mean to say the former.


Eric H. - [email protected] - http://blog.segment7.net
This implementation is HODEL-HASH-9600 compliant

http://trackmap.robotcoop.com

Eric H. wrote:

Don’t call to_str, call to_s.
restriction. We’re trying to solve two different problems – I want
Exactly my point. I think we’re misunderstanding each other here; if

never #empty?, …
That’s why you use the return value of #to_str. If only String had
#to_str, then why have it in the first place? Why not just check the
class?

I’m not saying this is how everybody should do it, I’m just saying that
it’s a very smart way to handle types – duck typing being a very
similar way.

Cheers,
Daniel

Eric H. wrote:

def foo(obj)

I favor using #to_str when I want to treat an object like a
words, #to_str should only be implemented on objects that are
@first, @last = first, last
more information, then it can have #to_str. Otherwise, stick to
#to_s.
A Name is not a String. You don’t #each it, you don’t #chop it, its
never #empty?, …

That’s why you use the return value of #to_str.

You don’t call #to_str, String calls #to_str when it needs to.

So the implementation of e.g. String#+ is wrong? Or is it okay for a
method on String to call #to_str on its arguments?

I’m not saying this is how everybody should do it, I’m just saying
that it’s a very smart way to handle types – duck typing being a very
similar way.

It isn’t a smart way to handle types, it is a broken and wrong way to
handle types.

I’m sorry, but I have to disagree. Read the RomanNumeral example again
– it’s an integer, but it doesn’t inherit from Integer.

Daniel

On Aug 8, 2006, at 12:45 PM, Daniel S. wrote:

a method on String to call #to_str on its arguments?
I suggest you read through the archives to discover when to use
#to_str and when to use #to_s. I’ve linked to matz’ discussion of
the matter several times. The short version is, if you call #to_str
or implement #to_str you are probably doing something wrong.

I’m not saying this is how everybody should do it, I’m just
saying that it’s a very smart way to handle types – duck typing
being a very similar way.
It isn’t a smart way to handle types, it is a broken and wrong way
to handle types.

I’m sorry, but I have to disagree. Read the RomanNumeral example
again – it’s an integer, but it doesn’t inherit from Integer.

Numeric duck types use #coerce because #to_int won’t cover all the
bases. See the earlier thread on #to_i vs #to_int, and try:

5 + Roman.new(6)

You’ll find that #to_int does not do anything for you.

This is why you shouldn’t call or define #to_xxx methods, how and
where they are supposed to be used is confusing, convoluted and error-
prone. They aren’t supposed to be used where you think they are
supposed to be used.

PS:

I can’t think of a legitimate reason to define a RomanNumeral class.
It would be easier to add Integer#to_roman that returns a String and
Kernel#Roman that parses a String into an Integer.

For bonus points, define #const_missing to parse a constant that is
also a Roman numeral.


Eric H. - [email protected] - http://blog.segment7.net
This implementation is HODEL-HASH-9600 compliant

http://trackmap.robotcoop.com

Some of the folks here might want to take a look at an article I just
posted to my blog about the subtleties of duck types:
http://talklikeaduck.denhaven2.com/articles/2006/08/10/ducks-can-be-subtle-birds

I started this ruby oriented blog a week or so ago, partially prompted
by the “I’ll Have the Duck” thread here. I’ve been a little reluctant
to expose it since it’s hosted on my home server, which I’m still
trying to get completely back to speed after an Ubuntu upgrade which
didn’t go completely well.

On Aug 9, 2006, at 1:25 AM, Robert K. wrote:

considerably more consideration than their counterparts #to_i and
#to_s - and you’ll rarely see them implemented.

The standard lib has several classes which define these methods:
you can try it out:

ruby -e ‘%w{to_int to_str}.each {|mn| print "\nMethod ", mn, “:\n”;
ObjectSpace.each_object(Module) {|cl| p cl if cl.instance
_methods.any? {|m| mn == m}}}’

In 1.9, Exception no longer implements #to_str, leaving only String
and NameError::message.


Eric H. - [email protected] - http://blog.segment7.net
This implementation is HODEL-HASH-9600 compliant

http://trackmap.robotcoop.com

I sort of agree to both of you and I think despite this thread you maybe
not too far away from each other.

On 09.08.2006 00:19, Eric H. wrote:

Numeric duck types use #coerce because #to_int won’t cover all the
bases. See the earlier thread on #to_i vs #to_int, and try:

5 + Roman.new(6)

You’ll find that #to_int does not do anything for you.

But if you define a class Roman #to_int should be part of it because a
roman can actually be treated as an integer. Of course you’ll also have
to implement #coerce and probably several others.

This is why you shouldn’t call or define #to_xxx methods, how and where
they are supposed to be used is confusing, convoluted and error-prone.
They aren’t supposed to be used where you think they are supposed to be
used.

I agree that implementing and invoking #to_int and #to_str needs
considerably more consideration than their counterparts #to_i and #to_s

  • and you’ll rarely see them implemented.

The standard lib has several classes which define these methods: you can
try it out:

ruby -e ‘%w{to_int to_str}.each {|mn| print "\nMethod ", mn, “:\n”;
ObjectSpace.each_object(Module) {|cl| p cl if cl.instance
_methods.any? {|m| mn == m}}}’

I can’t think of a legitimate reason to define a RomanNumeral class. It
would be easier to add Integer#to_roman that returns a String and
Kernel#Roman that parses a String into an Integer.

IMHO you can take at least two different positions here - which one is
more appropriate probably depends on the problem to solve. You can view
a roman number as another representation of an integer value. In that
case Erik’s approach is more appropriate. You can as well view a roman
number as something specific if it has properties that are not covered
by standard integer classes. In that case it deserves a class of its
own including #to_i, #to_int, #coerce, #eql?, #==, #hash, #<=>, #to_s
and maybe more.

Kind regards

robert