From: Robert Dober Date: 2006-04-02T22:54:12+09:00 Subject: Re: Typed Parameters ------=_Part_3082_29789825.1143986049576 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable Content-Disposition: inline On 4/2/06, Austin Ziegler wrote: > > On 4/2/06, Robert Dober wrote: > > On 4/2/06, Daniel Nugent wrote: > >> Well, I think you should allowed to put a selective effect on the > >> remaining arguments, but it should at least allow you to be a little > >> smarter than simply checking one single Type. I'd like to see you > >> able to check against multiple types as well as methods and > >> combinations thereof, like > >> > >> def foo(arg1 : (Array and :custom_array_method) or Hash or > >> :special_method) > >> > >> Then at least it's simply a syntactic convenience for writing > >> respond_to? and kind_of? calls. And, logically, you should be able > >> to assign these parameter checks to a variabe so you can reduce the > >> duplication of them, although I don't have a clue as to what a good > >> syntax for that would be... Maybe something like: > >> > >> type_check =3D TypeCheck.new do |var| > >> case var > >> when Array > >> return true if var.respond_to? :custom_array_method > >> when Hash > >> return Hash > >> else > >> return true if var.respond_to? :special_method > >> end > >> return false > >> end > >> > >> And, of course, you can do any checking you want in the block. You > >> could then do this: > >> > >> def foo(arg1 : type_check) > >> def bar(arg1, arg2 : type_check) > > > I quite agree, showes my how helpless I braught this up. :-( Should > > have named the thread "Easier ways to enforce contracts". Is that not > > strange that we think types immediately and than are afraid of ancient > > Computer Science History. > > In Ruby, at least, inheritance is not a guarantee of a contract (even on > objects derived from core classes). That is a very delicate point too, I strongly feel that a developper shoul= d *know* what kind of protocol an object is implementing and should be able t= o enforce this at a given point. By extending core classes (and I love to do it BTW) she is taking great risks. If you do not agree, that means to me that you are probably a very very talented designer, but do you not know error prone people like me? It's far better to think in terms of > what an object must *do* in your method, rather than to try to consider > what they *are* in your method. An example might help. > > Transaction::Simple has a debug mechanism. It expects an object that can > respond to #<< for output (and I believe *checks* for it). By default, > that means that I support String, Array, IO (and most, if not all, > descendants), and StringIO. I explicitly wanted it this way, whereas if > I had placed a class check (and make no mistake: most people are > familiar with statically typed languages and the addition of this will > make people think of Ruby in terms of static typing), might be and the way I presented it, for sure, real big mistake of mine. I would have > prevented the use of all but the class(es) that I explicitly specified. So you would not, why should you? There is no static typing, there is only the possibility of *early* renforcement of contract. It might be a bad idea anyway, but I did not reach you. This is completely clear from your very elaborate answer, which I appreciate. Look at it like this when we ask to check for certain conditions at certain points by means of assertions we have normally good reason to do so, right??? I belive that the moment when a message is sent is often the ideal moment t= o reinforce such constraints. def foo(x : {|para| %r{Robert is a real egghead} =3D~ x}) I can of course write that on my own in the next line, but it would just be against my laziness Maybe it is a feeling that I need help for my shortcomings as a programmer. I thaught "contract enforcing mechanisms" might allow for an easier way to program defensively. But I might be caught in too old paradigms, I really feel that you feel thi= s way and I will take it quite seriously. Unless you have a *damn* good reason to restrict a method to a > particular class, you shouldn't be doing any sort of checking at all > that way. Checking on method presence (e.g., #respond_to?) is more > useful (but only partially so; you should check the #arity of a method), > but even that fails if the person forgets to implement #respond_to? when > they've implemented #method_missing. It's also an extra run-time check > that, well, Ruby already does for you. Meta programming might ruin it, singleton methods might ruin it, we should use only final classes and methods as in Java ;) Now you see I understand this, your reaction, it might be a very sound one. Make no mistake: I'm not afraid of static typing. I just think that it's > useless in the context of real software development. Agreed I've been > developing software for ... a long time. Professionally a little less > than that. ;) I really *cannot* think of a time when I found that static > typing did anything except get in my way. And you never used assertions neither as you stated above? > My programs often pass objects of an unexpected "contract, behavior, > > type, mixin interface" you name it. I would like a modern language to > > help me, the *stupid* programmer to be more *effective* Ruby claims > > that. > > It's called "unit tests." If your programs are often passing objects > that don't behave the way you want those objects to behave, then it's a > bug that you should be ensuring doesn't come out of the caller; in Ruby > it's often far more efficient to ensure that the caller doesn't screw up > instead of the callee doing checks ... that Ruby already does. This is > one reason I'm so consistently opposed to the Eater Nil that is > periodically proposed: it changes the behaviour to silently fail instead > of noisily fail if I get something I'm not supposed to (usually a nil; > in the stuff that I've developed, you won't typically get a Real Object > that doesn't work). I take this point at 100%, I might be biased by bad design methods. > So let me suggest the following syntax > > > > def foo (bar, foobar) is a shortcut to > > def foo( bar, foobar : Object) which is a shortcut to > > def foo( bar, foobar : { |para| Object =3D=3D=3D para } ) so we can pu= t any > > constraint in there. > > > I still think that is a good thing for readability and maintenance and > > *nobody* would be forced to use it. Furthermore we are not forced to > > use the block inside the formal parameter list, it would just give us > > the opportunity. > > It's exceedingly ugly. It's also no better, in implementation terms, > than placing the contract enforcement as lines of code at the top of the > method. Except that the latter is clearer, explicit, and infinitely more > maintainable than writing a miniprogram in the parameter list. (And can > easily be extracted to a unit test where that sort of thing probably > belongs in most Ruby code.) Well I can live with this statement, I was quite aggressive too ;) Don't get me wrong: there are times when specifying a type is necessary. > Those types when you're dealing with pure Ruby are vanishingly small. > I'm looking at implementing some metacode for PDF::Core that includes > class specification (because the PDF specification has a defined number > of object classes). If I'm interacting with SOAP, it is useful to > specify the types/classes that I'm interacting with. > > (I would also suggest that if you're going to allow a block for this > sort of thing, you need to actually allow for inter-parameter validation > as well, e.g., if bar is String then foobar must be Fixnum; if bar is > Fixnum, then foobar must be String. Of course, that gets into more > complex validation, which really does underscore my point that this > behaviour belongs in the method itself. Maybe we can have precondition > and postcondition sections of methods, but putting all this in the > parameter list is ugly and nonsensical.) Well no, that was not intended , please remember that I *never* wanted any obligation to check. I wanted a tool, and probably it is not useful, you quite convinced me. > I really fail to see why this is inhibiting the language. Such > > programs fail explicitly and early, which is better (I claim) than > > maybe failing later or just not behaving as expected. > > Ah. That's where you're wrong: such programs would not fail any earlier > than they do now. The singular "advantage" to statically typed languages > is that they fail during the compile stage if there's a mismatch. Your > method still won't fail until it is called. That was intened like this, it would fail when called and not maybe three stack levels later, but as you said already, I can enforce the contract myself on crucial points. -austin > -- > Austin Ziegler * halostatue@gmail.com > * Alternate: austin@halostatue.ca > Thanks a lot for your precious time taken. Robert -- Deux choses sont infinies : l'univers et la b=EAtise humaine ; en ce qui concerne l'univers, je n'en ai pas acquis la certitude absolue. - Albert Einstein ------=_Part_3082_29789825.1143986049576--