Ruby's duck typing

S

stephan.zimmer

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
 
R

Robert Dober

On Thu, Nov 27, 2008 at 6:10 PM, stephan.zimmer
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
--=20
Ne baisse jamais la t=EAte, tu ne verrais plus les =E9toiles.

Robert Dober ;)
 
S

Sean O'Halpin

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
The short answer is no.

A technique that a (select ;) 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
 
B

Brian Adkins

stephan.zimmer said:
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)?

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.
 
R

Robert Dober

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

--=20
Ne baisse jamais la t=EAte, tu ne verrais plus les =E9toiles.

Robert Dober ;)
 
B

Brian Adkins

Robert Dober said:
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
:)

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"
 
R

Robert Dober

Robert Dober <[email protected]> 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 :(
Can you give an example please.

Cheers
Robert


--=20
Ne baisse jamais la t=EAte, tu ne verrais plus les =E9toiles.

Robert Dober ;)
 
J

Jesús Gabriel y Galán

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.
 
B

Brian Adkins

stephan.zimmer said:
Robert, Sean, Brian,

many thanks for your answers and the very interesting discussion.


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.


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.
 
R

Robert Dober

B

Brian Adkins

Robert Dober said:
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 :)
 
R

Robert Dober

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.ods.

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 :)
Sure agree we do not have to prove that Ruby is powerful enough :) and
the link you were presenting is a little confusing to me.
Cheers

Robert
 
J

Jörg W Mittag

Robert said:
[Message Passing vs. Multimethods]
Sure agree we do not have to prove that Ruby is powerful enough :) 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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,904
Latest member
HealthyVisionsCBDPrice

Latest Threads

Top