That's a pretty good example. It's easy to effect because it's based
on the single method #<<. And I've used it too.
Note the subtle shift here, the TYPE consists of a set of expected
behavior, in this case the single method #<<. This set of behavior is
like a job requirement. This type arises out of the USE of the object
by somone, not the implementation of the object. This is the
'role-based' typing I wrote about years ago in
http://talklikeaduck.denhaven2.com/files/TypesFromTheClientsViewpoint.PDF
One way of thinking about this is that there's no need to constrain
how we think about 'type' to the class implementation hierarchy.
Constraining subclasses to be subtypes which struggle to meet the
Liskov Substitution Property might be the only way of thinking about
things in C++/Java, but it's overly constraining, and even in those
other languages, it turns out to be a weak version of reality, there
are counterexamples of LSP even in Java and C++.
http://talklikeaduck.denhaven2.com/articles/2006/08/10/ducks-can-be-subtle-birds
or see:
http://c2.com/cgi/wiki?LiskovSubstitutionPrinciple
If you're up for a rather long discussion about what LSP does/doesn't
should/shouldn't mean. And what a subtype is/isn't.
Look at Alistair Cockburn's analysis in the c2 page of whether or not
Square is a subtype of Rectangle (or the other way around).
Ummm, how about rdoc commentary.
Seriously, it seems to me that the best that even a strongly typed
languages can do isn't much beyond syntactic type matching. There's
more to avoiding errors than just having matching signatures between
caller and callee. A lot of the discussion on the c2.com (Ward's
wiki) site is about how LSP falls down when the behavior changes even
though the signature doesn't.
Sometimes the right approach is that of a surgeon, assisted by a pathologist.
http://talklikeaduck.denhaven2.com/articles/2006/07/26/my-favorite-duck-typing-story
I'm talking about the close coupling (temporal and otherwise) between
codeing and testing which is found in all of the agile methods.
In other words don't overthink things, take a shot based on general
knowledge and be prepared to deal with the consequences.
No fair, you're getting back to the OPs example <G>
Seriously, I think that discriminating actual positional arguments
based on type discrimination is a separate issue, and I'm not sure
that it's that useful.
Positional parameters make a lousy technique for expressing variability.
Well, that's kind of my point. We use a string to represent a file,
even though it's not a file. Our representation don't necessarily
correspond to actual types.
Nice. You were able to put duck-typing to better work for us. But I
want to emphasis my point here. You were able to do this by thinking
in terms of *types*. When accepting parameters, you still think "a
File-like thing". With you're code it can now be a String or a File.
I'd argue that it's not really a file-like thing, it's an object which
represents a sequence and which has a method << which adds an object
to the end of the sequence.
It's not really limiting to specify types in method interfaces. We are
rather limited by not having good means to properly specify those
types.
Once again, I'd suggest rdoc commentary, and test cases, rather than
trying to come up with a means of 'properly' specifying such types.
Particularly if 'properly' means something which would produce a
compiler warning, which in the case of Ruby would appear only
milliseconds before the exception which would be thrown if the type
specification error wasn't a false negative and the compiler didn't
check.
So understandably we opt not to specify them at all. It's
always possible to have an object pretend to be something else:
class Chameleon
def self.===(other)
true
end
end
To be honest I'm not really that fond of the expression either, for
one thing I get the impression that it makes people think too much
about everything being a duck, instead of geese, hammers, etc.
Actually that was Kent Beck not Alan.
Ah, right you are. And I'd like to know too. Have Ruby advanced "duck
typing" or has just given us a lite version?
I'd say that mixins have a significant impact on being able to combine
implementation while breaking out of the tyranny of a hierarchy.
Mixins do tend to help cluster protocols, but they don't REALLY
provide anything more in the way of typing.
Consider some of the recent discussions about Array vs. Hash and the
reject/select methods. Just because two classes include Enumerable
doesn't make their instances substitutable for each other in all
contexts.
I'm not really arguing against duck-typing. I just don't think it's
necessarily in opposition to specifying optional parameter types. If
the the whole duck-typing idea was more deeply pursued we might even
find it quite intuitve to do so, using Mixins:
def foo(a < Enumerable)
But see the warning above.
Hmm me bad, I do not understand this, could you mabye give an example please?
A method probe is something like this:
class Decoy
private *instance_methods
attr_accessor :_record
def initialize()
@_record = []
end
def method_missing(sym, args, blk)
@_record << [sym, args, blk]
end
end
Now I can take any method I want which takes a parameter and drop the
decoy in:
def foo(x)
x.to_s
end
d = Decoy.new
foo(d)
d._record #=> [[:to_s, nil, nil]]
In this way we can see what kind of "duck" a parameter requires.
This isn't perfect however because of possible side-effects and
conditionals, which can skew the results. We would require a dry-run
mode and some dynamic means of dealing with condition statements (eg.
a decoy would split down both paths of any condition). It's not an
easy problem, but I don't think it is impossible either --at least not
impractically so.
Well if you think so , go ahead and pursue it. <G> I think that it's
a pretty big achilles heel.
I'm not sure how this would be used other than as part of a
development tool, you wouldn't want to do this at runtime would you?
Why not just use the regular test tools with the real objects. This
seems to me like an actor sending a cardboard cutout of himself to an
audition instead of going himself.