How to ducktype a Hash?

A

Ara.T.Howard

I think that could well work in practice, but wouldn't call it duck
typing. You're not asking it to quack up front; you're checking its
module ancestry -- which, I hasten to add, may be exactly what you
want to do, but isn't the same as what a duck typing approach would
lead you to.

Let me try to flesh this out with a companion example:

class UnHashlikeError < Exception
end

module Hashlike
alias :get_by_key :[]
end

class Hash # probably inadvisable, as Paul Brannan has
include Hashlike # recently pointed out...
end

# ...

value = begin
obj.get_by_key(k)
rescue NoMethodError
raise UnHashlikeError, "Object isn't hashlike"
end

or something like that. The basic idea here is: get your ducks in a
row, so to speak, by manipulating the object universe so that a
certain type or subtype of object understands a certain message, then
at runtime, send that message and branch on the consequences.


David

hmmmm... i see where you are coming from, but actualy disagree. the way i see
it duck typing is really not extremely different from interfaces. it's
basically just saying 'this thing is a duck if it quacks like a duck'. in
sean's case he's asking if an object

quacks (#[])

like (#[] any_object_not_just_indexes)

a duck (Hash)

in otherwords he is __exactly__ wanting to know if object posses certain
methods with certain signatures (a #[] methods which takes any 'ol object for
instance). this is asking if an object implements an interface - albeit a
small one. in fact duck typing has always struck me as objects implemnting
fine grained (and perhaps dynamic) interfaces. my approach is simply this:
ruby can't tell me this efficiently now, so, iff all objects can be known
before runtime, i'll simply mark them. i don't see this as checking ancestry
because one could have easily done this via some other form of marking:

class Hash
def is_hashlike?; true; end
end

class HashLikeA
def is_hashlike?; true; end
...
end

class HashLikeB
def is_hashlike?; true; end
...
end

etc.

case obj.respond_to? 'is_hashlike'
when true
when false
end

etc.

in fact. i can't imagine any efficient form of builtin duck typing which
would not be some sort of aggregate method/signature checking - it's simply
too costly to check each method and signature every time. but perhaps i'm
wrong... hopefully in fact ;-)

cheers.

-a
--
===============================================================================
| EMAIL :: Ara [dot] T [dot] Howard [at] noaa [dot] gov
| PHONE :: 303.497.6469
| A flower falls, even though we love it; and a weed grows, even though we do
| not love it. --Dogen
===============================================================================
 
J

John W. Long

why do you need to say 'it is hash like' ?
Wo'nt it be easyer to say 'I need this methods, so I'll just check
them' ?

BTW, probably the simpler solution is :

x.methods =={}.methods
:)
This is even worse than object.kind_of(Hash). The whole point of "duck
typing" (as I understand it) is that you don't check if an object has
certain methods. You just use them. Ruby will throw an error if there's
a problem.

The only case where it is advisable to use a form of :responds_to is
when you want to have different functionality based on the kind of
object that was passed in. A good use of this concept uses as few checks
as possible to determine the "kind" (I'm using the term loosely here) of
object.

Perhaps the question you should be asking if you have one method that
does multiple things based of the "kind" of object is, is your design is
flawed? Perhaps your objects themselves should know how to do whatever
operation your method that does multiple things does.
 
S

Sean O'Dell

It's not my classes that are the problem, it's all of the other
objects that
respond to [] and []=, but which aren't hash-like (they're array-like,
and
even then it's anyone's guess as to the functionality).

Then it's not your problem! Code on, young turk.

No, it IS my problem. My code quietly does some really undesired things when
non-hash-like objects get to this one point in code. I hate this attitude
about typing. I know you mean well, and it's been talked out to death, but I
don't buy the whole thing about "if it has the methods, it must be fine."
It's clearly NOT fine. If an array or a string, both which have the []
methods I need, get to this one place, I drop a whole branch of data being
passed around. The ideal is to catch it as an error, and remedy the
situation by finding out how the data came in that way in the first place and
prevent it, or perhaps just detect it and perform some special case. As it
is, I can't tell for sure when an object really DOES act like a hash.

Sean O'Dell
 
S

Sean O'Dell

Sean O'Dell said:
The problem isn't that the object doesn't respond to the methods I
need, it's that it DOES respond to the methods I need, but the
methods don't do what I expect them to do. In this case,
duck-typing simply doesn't work for me. I found this error through
duck-typing, but it existed for weeks before I realized that some
objects were quietly trashing sections of data I was passing around,
because strings, arrays and others all respond to [] and []=, but
DON'T contain objects quite the same way hashes do.

I thought the idea was that if you unit test each piece as you write
it, these sorts of things are usually caught in time.
I just don't like this duck-typing business. I can handle dynamic
typing, but I wish there were something more solid about the typing,
at least a way to know when an object implements a certain interface
or not, so I can tell an array-like object from a hash-like object.

As others have suggested, you don't HAVE to use duck-typing, but if
you choose that path, you need to provide your own type checks, and
shove all your classes in the right place in the right heirarchies.
However, anecdotal evidence (my own and by what others have said here
before) suggests to me that usually type checks have been weened from
in favor of a good unit test. Hence, I imagine, the lack of
strict-typing facilities in the language.

Unit tests are not the solution. I don't even do them anymore. They really,
really slowed me down and I found I still had just as many bugs to chase
down. They never saved me any headaches.

Sean O'Dell
 
S

Sean O'Dell

This is even worse than object.kind_of(Hash). The whole point of "duck
typing" (as I understand it) is that you don't check if an object has
certain methods. You just use them. Ruby will throw an error if there's
a problem.

Which always sounded fine to me until I was actually exposed to this kind of
"typing." The problem is, the methods [] and []= are there, they just don't
do what I need them to do. Duck typing completely and utterly don't help,
and Ruby has no way to tell me when an object's [] and []= are hash-like or
something else. My solution is going to end being something like "tagging
all objects that I know in advance to be hash-like, and then looking for that
tag at that particular point in my code." That's a kludge to me.

Sean O'Dell
 
S

Sean O'Dell

in otherwords he is __exactly__ wanting to know if object posses certain
methods with certain signatures (a #[] methods which takes any 'ol object
for instance). this is asking if an object implements an interface -
albeit a small one. in fact duck typing has always struck me as objects
implemnting fine grained (and perhaps dynamic) interfaces. my approach is
simply this: ruby can't tell me this efficiently now, so, iff all objects
can be known before runtime, i'll simply mark them. i don't see this as
checking ancestry because one could have easily done this via some other
form of marking:

class HashLikeA
def is_hashlike?; true; end
...
end

in fact. i can't imagine any efficient form of builtin duck typing which
would not be some sort of aggregate method/signature checking - it's simply
too costly to check each method and signature every time. but perhaps i'm
wrong... hopefully in fact ;-)

This is the solution I am going with now. In fact, if Ruby itself had this
sort of "interface identification" built-in, it would suit me perfectly. I
don't believe that respond_to? really tells you much about an object's
interface. Two objects can respond to the same method name, but do
COMPLETELY different things. Duck typing doesn't work, and checking
respond_to? doesn't work. The only way to REALLY know if an object
implements a certain interface is if you can positively identify it directly.
Somehow, the object has to say "I implement the hash interface." Whether
that's just a simple flag, or if Ruby can somehow match it's methods to an
interface description, or whatever, I don't care. All I want to know is if
an object implements a certain interface. I don't care about ancestry, what
methods it responds to, and I don't like just calling the object's methods
and crossing my fingers. Errors slip through quietly like that, and except
for people who don't really care about that sort of thing, it doesn't work.
It just doesn't.

Sean O'Dell
 
L

Lloyd Zusman

Sean O'Dell said:
This is even worse than object.kind_of(Hash). The whole point of "duck
typing" (as I understand it) is that you don't check if an object has
certain methods. You just use them. Ruby will throw an error if there's
a problem.

Which always sounded fine to me until I was actually exposed to this kind of
"typing." The problem is, the methods [] and []= are there, they just don't
do what I need them to do. Duck typing completely and utterly don't help,
and Ruby has no way to tell me when an object's [] and []= are hash-like or
something else.

I, for one, totally agree with you.

Duck typing cannot work unless methods with the same names and
signatures behave in the same way in the various classes that implement
them. If the ability to do duck typing is deemed to be an important and
necessary feature of an object-oriented language, then the standard
libraries that are written for that language _must_ provide different
names and/or signatures for methods that perform different functions.

Since [] and []= have certain semantics for one set of classes
(array-like classes) and different semantics for other classes
(hash-like classes), then we must conclude that Ruby was not designed
with the duck typing in mind.

Therefore, the best we can say about the current implementation of Ruby
is that duck typing can be used in _some_ cases, but not all.

I'm not trying to say that this is a deficiency of Ruby. On the
contrary; my point here is that I think that Ruby is a very well
designed language. On the other hand, I believe that duck typing should
not be treated as the be-all and end-all for type checking in Ruby,
given that Ruby's standard libraries do not seem to have been designed
with duck typing in mind.


[ ... ] My solution is going to end being something like "tagging all
objects that I know in advance to be hash-like, and then looking for
that tag at that particular point in my code." That's a kludge to me.
 
L

Lothar Scholz

Hello Sean,

Sean O'Dell said:
The problem isn't that the object doesn't respond to the methods I
need, it's that it DOES respond to the methods I need, but the
methods don't do what I expect them to do. In this case,
duck-typing simply doesn't work for me. I found this error through
duck-typing, but it existed for weeks before I realized that some
objects were quietly trashing sections of data I was passing around,
because strings, arrays and others all respond to [] and []=, but
DON'T contain objects quite the same way hashes do.

I thought the idea was that if you unit test each piece as you write
it, these sorts of things are usually caught in time.
I just don't like this duck-typing business. I can handle dynamic
typing, but I wish there were something more solid about the typing,
at least a way to know when an object implements a certain interface
or not, so I can tell an array-like object from a hash-like object.

As others have suggested, you don't HAVE to use duck-typing, but if
you choose that path, you need to provide your own type checks, and
shove all your classes in the right place in the right heirarchies.
However, anecdotal evidence (my own and by what others have said here
before) suggests to me that usually type checks have been weened from
in favor of a good unit test. Hence, I imagine, the lack of
strict-typing facilities in the language.

SOD> Unit tests are not the solution. I don't even do them anymore. They really,
SOD> really slowed me down and I found I still had just as many bugs to chase
SOD> down. They never saved me any headaches.

Yes they do slow you down. No doubt about this.
You can see the value only later when you change parts of your code,
maybe if you move from hash like objects to special objects with mixin
module XYZ.
 
A

Aredridel

Duck typing cannot work unless methods with the same names and
signatures behave in the same way in the various classes that implement
them. If the ability to do duck typing is deemed to be an important and
necessary feature of an object-oriented language, then the standard
libraries that are written for that language _must_ provide different
names and/or signatures for methods that perform different functions.

I'd say I agree and disagree.

Duck typing is more like a powerful tool: You can cut yourself easily,
but you can do things with it that you cannot otherwise.

What you speak of is interface typing or protocol contracts. That's
another matter.

Duck typing is mostly absence of checks, so if you feel the need to make
an array-like class, and pass instances to something that expects
something relatively array-like, you can.

It's something that takes both care and good design. If something is
inherently simple, duck typing is powerful and useful because it does
nothing but remove restrictions.

In the case of REXML, it is a large and complex enough system that it's
easy to break without some type or interface checks. However, it's also
flexible from that. The question is -- is it useful to have
REXML::Node-alike classes, or should it be type checked? I think I'd
have been bitten less if types were checked, but I may get annoyed when
I go to use REXML and find I can't just pass in something similar and
have it work. We'll see.

A mixin might be a suitable substitute -- it's possible to do interface
checking with mixins. Amrita does it (with Amrita::ExpandByMember), and
I find myself wishing that ActiveRecord did the same. I find myself
checking for Enumerable at times, because it's a good interface
definition.

Duck typing isn't perfect everywhere. With highly type-dependent and
complex systems like REXML, I think it's an ill fit there. However, I'm
glad that in general, Ruby is so dynamically typed -- I do some strange
things that wouldn't work in other languages, and the ability for things
like dRb to be so lightweight is a testament to the philosophy.
 
J

Joel VanderWerf

Lloyd said:
Duck typing cannot work unless methods with the same names and
signatures behave in the same way in the various classes that implement
them. If the ability to do duck typing is deemed to be an important and
necessary feature of an object-oriented language, then the standard
libraries that are written for that language _must_ provide different
names and/or signatures for methods that perform different functions.

But "same" and "different" are on a continuum. Sometimes Array#[] and
Hash#[] are close enough that sharing their method name makes sense
(e.g., using hashes as sparse arrays).
Since [] and []= have certain semantics for one set of classes
(array-like classes) and different semantics for other classes
(hash-like classes), then we must conclude that Ruby was not designed
with the duck typing in mind.

[] and []= don't even have the same semantics from hash to hash, because
of default_procs, singletons, etc. An object may respond to all the
usual Hash methods, and the object may be a Hash in the sense of
obj.is_a?(Hash), but it still doesn't behave as expected.

Of course, this may be an extreme example that is irrelevant to a given
application. But it reinforces the point that it's really hard to encode
semantics within interface, in any language. Hence unit tests.
 
L

Lloyd Zusman

Joel VanderWerf said:
Lloyd said:
Duck typing cannot work unless methods with the same names and
signatures behave in the same way in the various classes that implement
them. If the ability to do duck typing is deemed to be an important and
necessary feature of an object-oriented language, then the standard
libraries that are written for that language _must_ provide different
names and/or signatures for methods that perform different functions.

But "same" and "different" are on a continuum. Sometimes Array#[] and
Hash#[] are close enough that sharing their method name makes sense
(e.g., using hashes as sparse arrays).

Yes, sometimes. Recall the statement in my original post where I said
that duck typing can indeed work in Ruby in _some_ cases ... but not
all.

Since [] and []= have certain semantics for one set of classes
(array-like classes) and different semantics for other classes
(hash-like classes), then we must conclude that Ruby was not designed
with the duck typing in mind.

[] and []= don't even have the same semantics from hash to hash, because
of default_procs, singletons, etc. An object may respond to all the
usual Hash methods, and the object may be a Hash in the sense of
obj.is_a?(Hash), but it still doesn't behave as expected.

Of course, this may be an extreme example that is irrelevant to a given
application. But it reinforces the point that it's really hard to encode
semantics within interface, in any language. Hence unit tests.

Although unit testing is, in my opinion, necessary and very helpful, it
does not operate in the same "space" as duck typing.

The way I understand it, duck typing is used at run time to decide how
to operate on a given object. Here's one example out of many:

If an object that is encountered within a given program is thought to be
something hash-like, that program might want to perform one set of
functions on the object; if that object is thought to be string-like,
perhaps a completely different set of functions might be performed on
it; etc. With a universe of classes that are well defined in certain
ways, duck typing can be used to achieve this end.

Unit testing, on the other hand, is not done at run time to determine
how to process a given object that is encountered in the program;
rather, it's used in order to help ensure that a program behaves
correctly given any and all inputs that it might be given.

Comparing unit testing to duck typing is, therefore, analogous to
comparing apples to oranges.
 
F

Florian Gross

Sean said:
No, it IS my problem. My code quietly does some really undesired things when
non-hash-like objects get to this one point in code. I hate this attitude
about typing. I know you mean well, and it's been talked out to death, but I
don't buy the whole thing about "if it has the methods, it must be fine."
It's clearly NOT fine. If an array or a string, both which have the []
methods I need, get to this one place, I drop a whole branch of data being
passed around. The ideal is to catch it as an error, and remedy the
situation by finding out how the data came in that way in the first place and
prevent it, or perhaps just detect it and perform some special case. As it
is, I can't tell for sure when an object really DOES act like a hash.

I think what you're getting at is this:

Duck typing isn't really "quacks like a duck", but rather "quacks" in
its current state.

Maybe it would be an interesting thing to be able to take a set of unit
tests and dynamically run them on the Object that you're getting...?

Regards,
Florian Gross
 
N

Nathan Weston

How about using the fetch and store methods? These work the same as
Hash#[] and Hash#[]=, but are unique to Hash.
Your other hash-like objects might not implement them, but they would
be easy to add.

Nathan
 
D

Dave Thomas

No, it IS my problem. My code quietly does some really undesired
things when
non-hash-like objects get to this one point in code. I hate this
attitude
about typing. I know you mean well, and it's been talked out to
death, but I
don't buy the whole thing about "if it has the methods, it must be
fine."
It's clearly NOT fine. If an array or a string, both which have the []
methods I need, get to this one place, I drop a whole branch of data
being
passed around.

Then you need to test to make sure your parameter is a Hash. Duck
typing isn't applicable, as your code is coupled to the behavior of a
particular class.

Cheers

Dave
 
J

John W. Long

Duck typing may not be the best thing for you. =) Sorry. Didn't mean to
start another thread war on this.
 
J

John W. Long

Sean said:
Then it's not your problem! Code on, young turk.
No, it IS my problem. My code quietly does some really undesired things when
non-hash-like objects get to this one point in code. I hate this attitude
about typing. I know you mean well, and it's been talked out to death, but I
don't buy the whole thing about "if it has the methods, it must be fine."
It's clearly NOT fine. If an array or a string, both which have the []
methods I need, get to this one place, I drop a whole branch of data being
passed around. The ideal is to catch it as an error, and remedy the
situation by finding out how the data came in that way in the first place and
prevent it, or perhaps just detect it and perform some special case. As it
is, I can't tell for sure when an object really DOES act like a hash.
The problem Sean is that even if you know the method has the right
signature you can't guarantee it has the same functionality. It remains
an exercise for the person that wrote the contorted object and is
passing it in to figure out how to make it work with your code. If it
needs to be more hash-like, then let him figure out he needs to pass a
hash. If he can get his weird object to work without it actually looking
or acting like what you think a hash should look like, why penalize him
because he doesn't implement it in a certain way?
 
G

Gavin Sinclair

Which always sounded fine to me until I was actually exposed to this kind of
"typing." The problem is, the methods [] and []= are there, they just don't
do what I need them to do. Duck typing completely and utterly don't help,
and Ruby has no way to tell me when an object's [] and []= are hash-like or
something else. My solution is going to end being something like "tagging
all objects that I know in advance to be hash-like, and then looking for that
tag at that particular point in my code." That's a kludge to me.

Well, that's like static typing, which is a kludge to many people.
The "problem" is that Ruby allows you to decide on the level of
type-/class-checking that you want.

If this were a static OO language (take Java for example) then you
would have to declare your method as taking a Map parameter, and it
sounds exactly like what you want. Then any piece of code that called
your method would need to ensure it was passing a Map. All that's
doing is "tagging all objects that I know in advance to be map-like,
and looking for that tag at that particular point in your code". The
difference is that the compiler is doing the checking, and the objects
are marked from the outset because they implement a certain interface.

So if interfaces is what you want, then use them. If the fact that
you have to do the work rather than the language makes it a kludge,
then so be it. But Ruby's getting along fine without taking the
narrow road.

Cheers,
Gavin

P.S. If it were my code, I would simply do class-checking in that
particular instance: raise an error if it's not a Hash (just at that
point, not everywhere in my code). Then, when it so happens that some
other type of object wants to get through, I'd weaken the interface or
strengthen the objects. It may be the easiest thing to do to force
"clients" of the code to pass in a hash. Anything hashlike should
have a to_hash method. Perhaps you can use that method as your
hash-like indicator. (Remember, though, that Hash also has a to_a
method...)
 
A

Austin Ziegler

Lloyd Zusman:
The way I understand it, duck typing is used at run time to decide how
to operate on a given object. Here's one example out of many:

Um ... not the way that I understand it. Duck typing, per se, isn't
necessarily at all about testing an object's class, type, or
interface. It is simply assuming that an interface is there on the
provided object(s) and using it in a particular way. If it doesn't
work, then someone screwed up. You could do some interface checking
to see if an object claims to have a particular method (what Sean
O'Dell is calling duck typing), but that is something that can never
really be trusted, in any language. (Maybe Eiffel.)

IMO, Mr O'Dell needs to consider why he is checking for a
"Hash-like" behaviour. As Nobu (?) suggested, this is more likely
indicated by #each_pair than anything else (Struct behaves that way,
although OpenStruct does not -- perhaps an oversight). After all,
Procs implement #[] -- but they aren't Array *or* Hash (and this is
because there is -- thankfully -- no () operator in Ruby).

I see no problem with checking for #kind_of?(String), or Array, or
Hash -- if you must absolutely have the semantics offered by those
classes, since they are fundamental. However, doing so will limit
your code rather severely, and there are alternatives in most cases.

-austin
 
S

Sean O'Dell

Sean said:
No, it IS my problem. My code quietly does some really undesired things
when non-hash-like objects get to this one point in code. I hate this
attitude about typing. I know you mean well, and it's been talked out to
death, but I don't buy the whole thing about "if it has the methods, it
must be fine." It's clearly NOT fine. If an array or a string, both
which have the [] methods I need, get to this one place, I drop a whole
branch of data being passed around. The ideal is to catch it as an
error, and remedy the situation by finding out how the data came in that
way in the first place and prevent it, or perhaps just detect it and
perform some special case. As it is, I can't tell for sure when an
object really DOES act like a hash.

I think what you're getting at is this:

Duck typing isn't really "quacks like a duck", but rather "quacks" in
its current state.

Maybe it would be an interesting thing to be able to take a set of unit
tests and dynamically run them on the Object that you're getting...?

It would be simpler to just have some typing.

Sean O'Dell
 
S

Sean O'Dell

No, it IS my problem. My code quietly does some really undesired
things when
non-hash-like objects get to this one point in code. I hate this
attitude
about typing. I know you mean well, and it's been talked out to
death, but I
don't buy the whole thing about "if it has the methods, it must be
fine."
It's clearly NOT fine. If an array or a string, both which have the []
methods I need, get to this one place, I drop a whole branch of data
being
passed around.

Then you need to test to make sure your parameter is a Hash. Duck
typing isn't applicable, as your code is coupled to the behavior of a
particular class.

The objects are not always a Hash per se, just objects with hash-like
functionality. But, as I said, other objects can slip in too, like strings
and arrays, which have [] and []= methods, but which don't act like hashes at
all.

Sean O'Dell
 

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

Members online

No members online now.

Forum statistics

Threads
473,982
Messages
2,570,186
Members
46,740
Latest member
JudsonFrie

Latest Threads

Top