From: Rick DeNatale Date: 2007-06-28T03:11:52+09:00 Subject: Re: Named/positional method args On 6/27/07, Trans wrote: > On Jun 27, 1:45 am, "Robert Dober" wrote: > > On 6/27/07, Trans wrote: > > > > > > > > > On Jun 26, 6:22 pm, dbl...@wobblini.net wrote: > > > > > Is it limiting? Rubyists (myself included) love to wax grand about the > > > flexibility of Ruby's type dynamics. But I have to be honest, over the > > > years it's become clearer to me that reality is in fact more limiting. > > > Beyond symbol_or_string.to_s, and the like, how extensively do we ever > > > see duck-typing playing a roll in our code? > > > > I do not know Tom, one application springs into mind, I am generating > > iptables commands from a DSL. The outpout is done via #<< to an object > > (defaulting to $stdio) for testing I just set that object to an array. > > > > A rare occasion, yes, but a very serious feature indeed. > > 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). > > > Granted Class-base typing can be too constricting, just as respond_to > > > typing can be too granular. What would be optimal is something more > > > fluid, that can encompass them or any shade inbetween --a way to > > > specify what something represents, not just how it's encoded. 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. >>Take our > > > example above. We want two arguments, an Integer which represents an > > > *index* and a string which represents a *file*. Now there are a few > > > ways to represent a file actually, File.open(), Pathname.new(), > > > probably others. And yet, duck-typing isn't going to do a bit of good > > > if one of those is passed in instead of the String representation. No fair, you're getting back to the OPs example 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. > > That, IMHO, is because your example is not about Duck Typing, as > > Strings and Files do not quack and walk alike. > > 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. > > > We could make them so of course, and I wonder why I have not done this > > before, given that this is indeed a common pattern in my programs too: > > > > class File > > alias_method :open_as_file, :open > > def to_file; self end > > end > > > > class String > > def open_as_file *args, &blk > > File.open(self, *args, &blk) > > end > > def to_file *args > > File.new( self, *args) > > end > > end > > 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 > > > I do not like the expression "Duck typing" too much (Rick's dog > > notwithstanding ;) I feel more like "using protocols". > > Ruby gives you the power to implement those protocols but you have to > > implement some of them yourself ;) 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. > > > I think Ruby is a great experiment in this area, and undoubtedly it > > > shows some promise. > > > > Smalltalk? 35 years ago, I think Rick confirmed the now famous Alain > > Kay quotation about Smalltalk beating Java, but being called Ruby. Actually that was Kent Beck not Alan. > > I am however eager to learn subtle differences in the two "duck typing" models. > > Ah, right you are. And I'd like to know too. Have Ruby advanced "duck > typing" or has just given us a lite version? > > > > In particular, mixins have a significant impact > > > our ability to use duck-typing. They allow us to think in terms of > > > common functionality --all thing Enumerable, or all things Sortable, > > > etc. It would be interesting to see how much further this kind of > > > generalization could be taken. Are there potential mixins we are not > > > using? 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. > > > Slightly aside, but I would also point out that a language which is > > > serious about duck-typing would do well to support complete method > > > probing. We should be able to pass "decoy" parameters to any method, > > > acting in DryRun fashion, and record all possible calls made on them. > > > I suspect such a tool would be invaluable in ascertaining the common, > > > essential methods we apply to different representations. > > > > 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. 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. -- Rick DeNatale My blog on Ruby http://talklikeaduck.denhaven2.com/