Ruby's duck typing


#1

I would like to represent certain data by a list; to this end I let
class SomeData inherit from Array:

class SomeData < Array
    def get_field
        self[2]
    end
end

(I want to keep it simple, therefore, the example might look silly.)
Since the “get_field” method is not universal I don’t want to reopen
class “Array”. If I write

SomeData.new([1,2,3]).get_field

everything is fine. If I, however, try to do

[1,2,3].get_field

I get an exception “NoMethodError”, which, of course, is not
surprising.

My question is: is there a way around this, that is, to simply write
[1,2,3] to denote a constant of type “SomeData” instead of always
writing “SomeData.new([1,2,3])” (without reopening Array)?

Thanks a lot,

Stephan


#2

On Thu, Nov 27, 2008 at 6:10 PM, stephan.zimmer
removed_email_address@domain.invalid wrote:
Personally for some time now, and thanks to the learned opinions here
I have started to favor delegation over inheritance especially when
it comes to core classes.
Look at this approach

http://pastie.org/325446

hopefully you find it helpful.

Cheers
Robert

Ne baisse jamais la tête, tu ne verrais plus les étoiles.

Robert D. :wink:


#3

“stephan.zimmer” removed_email_address@domain.invalid writes:

Since the “get_field” method is not universal I don’t want to reopen

My question is: is there a way around this, that is, to simply write
[1,2,3] to denote a constant of type “SomeData” instead of always
writing “SomeData.new([1,2,3])” (without reopening Array)?

Do I understand you correctly that you want a function that works on
instances of SomeData and Array similarly without re-opening Array?

Rather than:

SomeData.new([1,2,3]).get_field
[1,2,3].get_field # doesn’t work

How about the following?

get_field(SomeData.new([1,2,3]))
get_field([1,2,3])

In other words, maybe you want a function that knows how to
“get_field” given any indexable sequence.

 1  class SomeData < Array
 2    def self.get_field list
 3      list[2]
 4    end
 5  end
 6
 7  x = SomeData.new([1,2,3])
 8  y = [4,5,6]
 9
10  puts SomeData.get_field(x)
11  puts SomeData.get_field(y)

Ruby doesn’t support generic functions, so there’s a limit to this
approach, but depending on how simple your real class is, it might be
a possibility.


#4

Ruby doesn’t support generic functions, so there’s a limit to this
approach,
Which limit Brian? I do not see anything Ruby cannot do where you
have to use generics in other languages.
Do I miss the obvious here?
Generics are used to create instantiations of a generic for given type
parameters. As there are no types in ruby
the method just takes the place of the generic function.

Robert


Ne baisse jamais la tête, tu ne verrais plus les étoiles.

Robert D. :wink:


#5

On Thu, Nov 27, 2008 at 5:10 PM, stephan.zimmer
removed_email_address@domain.invalid wrote:

Since the “get_field” method is not universal I don’t want to reopen

My question is: is there a way around this, that is, to simply write
[1,2,3] to denote a constant of type “SomeData” instead of always
writing “SomeData.new([1,2,3])” (without reopening Array)?

Thanks a lot,

Stephan

The short answer is no.

A technique that a (select :wink: few of us favour is to define factory
methods like this:

def SomeData(*a, &b)
SomeData.new(*a, &b)
end

so you can write:

a = SomeData [1,2,3]

You might also want to have a look at Ara Howard’s arrayfields:
http://www.codeforpeople.com/lib/ruby/arrayfields/. Latest version
docs here:
http://www.codeforpeople.com/lib/ruby/arrayfields/arrayfields-4.6.0/README

Having said all that, subclassing core classes tends to throw up a
host of fiddly issues. Robert’s suggestion to use delegation instead
is a good one where your class doesn’t need to be considered as a kind
of Array (in this case).

Regards,
Sean


#6

Robert D. removed_email_address@domain.invalid writes:

Ruby doesn’t support generic functions, so there’s a limit to this
approach,
Which limit Brian? I do not see anything Ruby cannot do where you
have to use generics in other languages.
Do I miss the obvious here?

No, you didn’t “miss the obvious” because I didn’t provide much detail
:slight_smile:

If you consider two models of OOP, message passing and generic
functions, Ruby/Smalltalk would be an example of the former, Common
Lisp would be an example of the latter.

With message passing, you can specialize the first parameter
(e.g. self). With generic functions, you can specialize on multiple
parameters.

As Paul Graham stated, "In the message-passing model, methods are of
objects, while in the generic function model, they are specialized
for objects.

The approach I mentioned has limitations with respect to inheritance
i.e. if “get_field” need to be specialized for various types. It was
more in the “style” of generic functions (not belonging to an
object), but without language support, so it’s going against the grain
a bit.

In many cases it’s a reasonable mechanism (i.e. a more functional
approach), and can allow for simpler reasoning about the function
since it doesn’t have dependencies to the class.

Generics are used to create instantiations of a generic for given type
parameters. As there are no types in ruby
the method just takes the place of the generic function.

“generic function” not “generic type”


#7

Robert, Sean, Brian,

many thanks for your answers and the very interesting discussion.

Look at this approach
http://pastie.org/325446
hopefully you find it helpful.

Ya, that’s really nice, also to me delegation seems more reasonable
than inheritance in many cases. I will utilize it. The “factory
approach” is also nice, I never thought about it. I think it gives
handy code.

As Paul Graham stated, "In the message-passing model, methods are of
objects, while in the generic function model, they are specialized
for objects.

It might be naive, but wouldn’t it be possible to simulate a generic
function by simply accessing the types of the parameters (using
“is_a?”) and then provide different function bodies for different
parameters?

Is there any document, paper, etc. which states more details about the
Ruby type system, as design motives, comparisons to other languages,
etc.?

Best,

Stephan


#8

On Fri, Nov 28, 2008 at 2:35 AM, Brian A. removed_email_address@domain.invalid
wrote:

Robert D. removed_email_address@domain.invalid writes:

There is an old joke: How do you call a dear without eyes? No idea!
And how do you call a dear without eyes and legs? Still no idea.
Unfortunately the later applies to what you say :frowning:
Can you give an example please.

Cheers
Robert


Ne baisse jamais la tête, tu ne verrais plus les étoiles.

Robert D. :wink:


#9

On Fri, Nov 28, 2008 at 12:25 PM, stephan.zimmer
removed_email_address@domain.invalid wrote:

As Paul Graham stated, "In the message-passing model, methods are of
objects, while in the generic function model, they are specialized
for objects.

It might be naive, but wouldn’t it be possible to simulate a generic
function by simply accessing the types of the parameters (using
“is_a?”) and then provide different function bodies for different
parameters?

For this you can take a look at facets/overload, which allows things
like:

class X
def x
“hello”
end

overload :x, Integer do |i|
  i
end

overload :x, String, String do |s1, s2|
  [s1, s2]
end

end

(taken from facet’s doc: http://facets.rubyforge.org/doc/index.html)

Jesus.


#10

Robert D. removed_email_address@domain.invalid writes:

On Fri, Nov 28, 2008 at 2:35 AM, Brian A. removed_email_address@domain.invalid wrote:

Robert D. removed_email_address@domain.invalid writes:

There is an old joke: How do you call a dear without eyes? No idea!
And how do you call a dear without eyes and legs? Still no idea.
Unfortunately the later applies to what you say :frowning:
Can you give an example please.

I have no idear what you’re talking about above, but the following
might help:

http://gigamonkeys.com/book/object-reorientation-generic-functions.html


#11

On Fri, Nov 28, 2008 at 7:25 PM, Brian A. removed_email_address@domain.invalid
wrote:

I have no idear what you’re talking about above, but the following
might help:
You got it :slight_smile:

http://gigamonkeys.com/book/object-reorientation-generic-functions.html
Well Ruby does not have method composition but everything, especially
the generic part, is super-seeded by the dynamic typing model.
Robert


Ne baisse jamais la tête, tu ne verrais plus les étoiles.

Robert D. :wink:


#12

“stephan.zimmer” removed_email_address@domain.invalid writes:

approach" is also nice, I never thought about it. I think it gives
handy code.

As Paul Graham stated, "In the message-passing model, methods are of
objects, while in the generic function model, they are specialized
for objects.

It might be naive, but wouldn’t it be possible to simulate a generic
function by simply accessing the types of the parameters (using
“is_a?”) and then provide different function bodies for different
parameters?

Don’t get me wrong - I love Ruby, but Ruby is slow enough already w/o
introducing an interpreted multiple dispatch mechanism. It certainly
could be done, but I wouldn’t recommend it. Double dispatch might be
worth considering, but that would get ugly with more params.

Simple functions that depend on duck typing wrt parameters works fine,
but if inheritance is needed, then using Ruby’s inheritance model
would be superior to simulating generic functions.


#13

On Fri, Nov 28, 2008 at 10:54 PM, Brian A. removed_email_address@domain.invalid
wrote:

comp.lang.ruby, not comp.lang.lisp, so back to our beloved Ruby :slight_smile:
Sure agree we do not have to prove that Ruby is powerful enough :slight_smile: and
the link you were presenting is a little confusing to me.
Cheers

Robert


#14

Robert D. removed_email_address@domain.invalid writes:

I have no idear what you’re talking about above, but the following
might help:
You got it :slight_smile:

http://gigamonkeys.com/book/object-reorientation-generic-functions.html
Well Ruby does not have method composition but everything, especially
the generic part, is super-seeded by the dynamic typing model.

I think you’ve reached an erroneous conclusion - Ruby’s dynamic typing
model certainly does not supercede generic functions. I’d suggest
taking another look at the article (focusing on the Multimethods
section), or finding other sources of info on generic functions.

This is not a slight against Ruby; both models have pros/cons;
although, generic functions are a generalization of message passing,
so, if anything, generic functions supercede message passing.

Anyway, I think I’ve caused us to veer off topic - this is
comp.lang.ruby, not comp.lang.lisp, so back to our beloved Ruby :slight_smile:


#15

Robert D. wrote:

On Fri, Nov 28, 2008 at 10:54 PM, Brian A. removed_email_address@domain.invalid wrote:

[Message Passing vs. Multimethods]
Sure agree we do not have to prove that Ruby is powerful enough :slight_smile: and
the link you were presenting is a little confusing to me.

Here’s my little attempt at explaining multimethods / multiple
dispatch.

In Ruby and most other single-dispatch OOP languages, methods “belong”
to objects, or, in the case of class-based OO languages like Ruby, to
classes of which the objects are instances.

So, we generally send a message like this:

receiver.message(param, anotherparam)

However, we could just as well use a procedure instead of a method and
call it like this:

message(receiver, param, anotherparam)

All the information is still there, the method body has access to all
the same parameters as it had before. The only difference is that in
the method example, the receiver parameter was passed implicitly into
the method body and was magically accessible via the special variable
keyword “self”, where in the procedure example it was passed
explicitly.

There is one important difference however, and it is one of the
pillars of OO: polymorphism.

The way that procedures are usually implemented, there can only ever
be one procedure with a specific name. With methods however, there can
be many methods with the same name, and which method to execute gets
determined at runtime based on the type (or class) of the special
receiver parameter. This is polymorphism, or, more precisely
single-dispatch runtime subtype polymorphism.

This is where multimethods come in.

A multimethod is a procedure like above. But there can be many
procedures with the same name! And which one to execute is
determined at runtime, not only by looking at the types of the first
parameter (like in single-dispatch OO), but by looking at the types of
all parameters! In that sense, multimethods are more powerful than
methods: you can write a multimethod which is only polymorphic in its
first parameter, which is exactly the same as with a method. But you
can also write a multimethod which is polymorphic in more of its
parameters, which you cannot do with methods. You can fake it,
though: the Visitor pattern is basically an implementation of
double-dispatch (i.e. polymorphism on two parameters instead of one)
for single-dispatch languages. Ruby’s coercion protocol is another.

Here is a simple example how that might work in Ruby. Remember the
coercion example in the Pickaxe book with the Roman numerals?
Basically, the problem is this: when I call

'IV'.to_roman + 3

that works, but

3 + 'IV'.to_roman

doesn’t. Why? Because my Roman class knows about Fixnums but Ruby’s
builtin Fixnum class doesn’t know about Roman numerals. To deal with
this, Ruby has this coercion protocol. In this case, Fixnum#+ calls
‘IV’.to_roman.coerce(3), which in turn returns something like [3, 4]
which can then be used to retry the original operation. With
multimethods, all of this would be unnecessary. The “+” method would
not be defined inside of Fixnum and Roman (and Float and String and
Array and …) but instead as a multimethod:

# This would be part of Ruby Core:

defmulti +(a, b)

def +(Fixnum f1, Fixnum f2)
  # do stuff
end

# This would be part of your library:

def +(Roman r, Numeric n)
  r.to_int + n
end

def +(Numeric n, Roman r)
  n + r.to_int
end

Basically, what multimethods do, is to free polymorphism from the
tyranny of the receiver.

We can go one step further, however: in CLOS multimethods can not only
dispatch on the type of all of their arguments, but also on the
value of all of their arguments. This enables something like this
classic example:

defmulti factorial(n)

def factorial(Integer n)
  return n * factorial(n - 1)
end

def factorial(0)
  return 1
end

In all of these examples, the dispatch function became more and more
powerful: in procedures, there is no dispatch function. In methods,
the dispatch function is basically a simple switch on the type of the
first parameter (although not that simple: it has to follow the
inheritance hierarchy, of course). In simple multimethods, the
dispatch function switches on the types of all arguments. In CLOS,
the dispatch function switches on the types and values of all
arguments.

However, in all of these cases, the dispatch function is a fixed part
of the language implementation. So, Clojure takes the obvious next
step: when declaring a multimethod, you can provide your own
dispatch function and you can do whatever you want with the arguments.
Switch on the first, switch on all, switch on the sum of the 7th and
42nd, switch on the phase of the moon …

In Ruby, that would look something like this (stolen from the Clojure
website):

defmulti encounter(animal1, animal2)
  return animal1.class, animal2.class
end

def encounter(Lion, Rabbit)(l, r)
  eat(l, r)
end

def encounter(Lion, Lion)(l1, l2)
  fight(l1, l2)
end

def encounter(Rabbit, Rabbit)(r1, r2)
  mate(r1, r2)
end

def encounter(Rabbit, Lion)(r, l)
  run(r)
end

And here the ubiquitous enterprise joke:

defmulti enterprise_hello_world()
  return rand <= 0.9, rand <= 0.5
end

def enterprise_hello_world(true, true)()
  puts 'Hello, World!'
end

def enterprise_hello_world(true, false)()
  sleep 10
  puts 'Hello, World!'
end

def enterprise_hello_world(false, *)()
  raise RuntimeError
end

Note the method body of defmulti: this is not the code for the
method, this is the dispatch function. Its return value will be
matched against the pattern definition in the first set of
parantheses. The method definitions have two sets of parameters:
first the pattern that is matched against the dispatch function, then
the formal parameters of the method.

Whereas simple multimethods free polymorphism from the tyranny of the
first argument, this frees it from the tyranny of the language
designer.

Anyway, that’s my little explanation of multimethods, from someone who
understands them as just as badly as you do (-:

It’s gotten a little long (note to self: start a blog, damnit!), I
hope it’s not too long; and I hope it’s helpful.

Cheers,
jwm


#16

On Sat, Nov 29, 2008 at 7:00 PM, Jörg W Mittag
removed_email_address@domain.invalid wrote:

It’s gotten a little long (note to self: start a blog, damnit!), I
hope it’s not too long; and I hope it’s helpful.
Very clear, thank you!
Robert


#17

On Sat, Nov 29, 2008 at 7:00 PM, Jörg W Mittag
<removed_email_address@domain.invalid removed_email_address@domain.invalid>
wrote:

(note to self: start a blog, damnit!)
I strongly second that :wink:

Thanks for the helpful explanation.

Regards,
Yaser S.