From: "Chris Uzdavinis" <chris / atdesk.com> > "Nat Pryce" <nat.pryce / b13media.com> writes: > > In Ruby, instead of defining classes or interfaces, you define > > protocols that objects should speak, and define those protocols > > outside the language. > > This is interesting. What distinction do you make between protocol > and interface? Can the protocol be independent from the interface? A protocol encompasses (a) more than one interface and (b) how and when the parties in a protocol can invoke methods on those interfaces, and what parameters they can pass to those invocations. > > You can then define useful classes or modules that implement those > > protocols, but a programmer can just as easily write the protocol > > from scratch. > > Yes, in that regard, every ruby function is like a C++ function > template. (Gotta make those C++ comparisons...!) > > template <typename T> void foo(T arg) { /*...*/ } > > Rather than an "interface" which is a fixed, hard coded set of > requirments, of which perhaps only a subset is actually used, > templates use what they call the "concept" approach. ANY arg can be > passed in provided that it supports all the necessary concepts which > the code places on it. (The difference is a compile-time check is > made, rather than runtime.) It is similar to a C++ template in that C++ templates are turing complete and dynamically typed. You can, if perverse enough, write complete programs in C++ templates and have then interpreted by the C++ compiler! Similarly, Ruby programs are turing complete and dynamically typed. However, Ruby has a human-friendly syntax and a far more convenient development environment :-) Just as with C++ templates, violating protocols in a dynamically typed language can result in obscure error messages. The C++ community is developing mechanisms to type-check templates as early as possible. In a dynamically typed language, unit tests fulfill the same role. > > An simple example of this kind of protocol is the relationship > > between the methods hash and eql? or the Enumerable mixin requiring > > the existence of the each method. > > These are good examples. Do you think that when we write Ruby code > that this type of mindset should be the primary kind of issues we > think about? Yes. This is the fundamental mind-set that I use when desiging OO code, whether in a dynamically typed language or a statically typed language. A statically typed language lets you define interfaces or abstract classes, but they don't actually let you define the *protocols* by which objects interact, and so are only of limited practical use. > How would you suggest we recover when an argument does not support the > protocol? Since Ruby is dynamic, object x may not support it now, but > if you try again later it might. Or vice versa. You don't recover. That's a programming error -- a bug. Programming errors are the fault of the programmer and should be caught by unit tests. If an error occurs at run time you should (a) write a unit test that fails because of the error and (b) fix the code so that the unit test passes. You now have better code and a more complete suite of tests. > There certainly is an element of uncertainty introduced along with all > the dynamic capabilities. Only if you don't have adequate tests. Dynamic code is a convenience, and a great one. However, the behaviour of a package will not magically change because it is written in dynamic code. The behaviour of the package should be specified both in terms of protocols, and by a good suite of unit tests. > The trick is keeping it balanced to where > you can know what's going on but still have flexibility. > I still have this uneasy feeling, though, that nothing can ever be > completely trusted, because perfectly working code can easily be > broken by other code in another file which is completely unrelated to it. Again, only if code is not properly specified and tested. Unit tests are an executable specification of how code should behave. > It seems you have to really know everything that is going on in a > program to know if it's going to work. Though there are Modules to > kind of separate things, it's not truly modular since if your code and > mine are running in the same program, my code can break yours, and > vice versa. > > The risk to me is not all just type, but behavior too. In a big > application it's unreasonable to expect everyone to study every line > of every file that everyone else is working on. > > So when someone modifies the behavior of Array class such that [] > becomes 1-based, for example, he breaks MY working code, which hasn't > changed. Again, use automatic test suites! Never do by hand that which a computer can do automatically :-) Cheers, Nat.