From: Caleb Clausen Date: 2005-06-01T23:06:06+09:00 Subject: Re: Intellisense and the psychology of typing Austin Ziegler wrote: > Type hints are primarily a > documentation feature. Let me repeat that for emphasis: it is a > documentation feature. If an optimizing Ruby compiler is able to > take advantage of it -- great. And I still fail to see the semantic difference between the two. Please explain, I'm still listening. In what case would a tool -- any tool -- be it compiler, interpreter, rdoc, ide, or whatever want to treat your 'type hint' differently than my 'type declaration'? As far as I'm concerned, they're different words for the same thing. Just because you want to put it in a comment is not a semantic difference; it's just syntactical. > If not ... too bad. This sort of attitude is unnecessarily harsh. Whether you care or not about all the same things someone else might, the fact remains that the exact same mechanism that's needed for what you want (documentation) can provide the information needed by an optimizing compiler. > DO *NOT* USE POLS in your argument. It doesn't apply. Matz has > indicated that it's just so much crap. The principle of least surprise is a good principle in language design. Unfortunately, everyone is likely to see it subjectively: principle of my least surprise. Matz interprets it this way, thus for ruby, POLS really means POLMS (principle of least matz surprise). Has matz said yet what is the least surprising to him, a type hint (or declaration) in a comment before the function, or inside the function header? I'm not aware that he has. Since I'm no good at reading matz' mind, (or his blog, for that matter) I tried to come up with an objective version of POLS, from the point of view of an average programmer from another language. Since POLS is really a form of appeal to authority, I also cited two other principles that I believe apply in this case. > Oh, please *don't* try to lecture me on this stuff, Caleb. Well, Austin, I will certainly lecture you if you seem to require lecturing. (If that's waht you want to call it.) You made a logical error in your argument. You equated static typing and class-based typing. They are the same in most languages, but need not (and should not, in my opinion) be the same thing in a ruby static type system. > 1. It looks like the stuff in other languages, but because Ruby > isn't statically typed (thank GHU for that), it won't have the > same meaning as it does in those languages. There are many dynamic languages with variable type declarations. Javascript and scheme leap to mind. There are many things in dynamic languages that don't have quite the same semantics as their equivalent in a static language, but the same or similar syntax nevertheless. Integer addition leaps to mind. Just because C also has integer addition, using +, does that mean that ruby must use some different syntax when adding integers, since it actually handles overflow? > 2. If it does have the same meaning in those languages, we're no > longer talking about Ruby. Every time you change the language, it's no longer the same language, but so what? > 3. It will encourage people to use it for the wrong reasons in the > wrong way. I addressed this in my last message. You need to provide further evidence rather than just repeat the same thing again. > 4. It becomes more than informational. I fail to understand what you mean by this word. A type declaration is informational. It informs a language tool what the expected type of a variable is. How is it more than that? > You haven't been paying attention. StringIO doesn't inherit from > String or IO, but it will act like either. If I add a #read method > and a #write method to a random object, it will act a lot like an > IO. If I add #to_str to said object, it acts a lot like a String. Yes, yes, I know. > The method signature is not related to the "duck > type." If you've used Ruby for any length of time, you recognise > that as well. I'm not Matz -- or even Dave Thomas -- but duck typing > is, ultimately, not *caring* about the type of object you're > provided. It's just using the type of object. I know what you say is gospel, but I think it's a bit of a simplification. Consider something like this: def process_it(file) ... file.read ... file.write end I've given the parameter a name that makes it seem that only a File is possible, but we like to be able to pass other things, so long as they adhere to process_it's view of file's method signature. (In this case, read and write.) This is what is usually referred to as duck typing. You can't really pass any kind of object to process_it. You can't give it an Integer, or (sans some kind of help) a String. The set of types that can usefully be assigned to file are in practice quite limited. And it's relatively rare that you actually want file to be able to be any object. There are cases, but it seems more common that the set of types is limited. If it really is any object, you have to limit yourself to methods of Object inside process_it. We could have type declarations like this: def process_it(file : IO|StringIO) #declare file to be one of 2 types end or this: def process_it(file : :read&:write) ... end The second form is what you'd want to use, mostly. It would declare that process_it takes a parameter that can read and write. No classes are mentioned in the declaration. Incidently, this part: :read&:write is exactly what java calls an interface. Notice how much smaller and cleaner it can be in ruby. Rdoc, or whatever your favorite tool happens to be, could read the type from a declaration just as easily as from a specially formatted comment. And this is the type of information that's useful to rdoc; when writing something that uses process_it, you like to know that it can take a param that knows read and write, rather than being told that it has to be a File (too restrictive), or (worst of all) Object (far too broad). > No, we can't. Interface declarations are simply wasted code time and > space. It's that simple. I'm sorry you feel that way, but I'm afraid you haven't convinced me. (Hint: a bald statement of fact, with no support, isn't sufficient.) I see you have a different view of java's interfaces. I believe that they are intended as a way to add multiple inheiritance to a single inheiritance system. A need which mixins in ruby satisfies better. But interfaces provide much the same capability (in a declare-everything-and-the-kitchen-sink ahead of time kind of way) in java that duck typing provides for ruby. In either case a library author can provide the user with the capability to pass in an object whose exact class isn't specifically known ahead of time; it just has to implement a certain calling protocol (or method signature). Ruby is nice in that you don't have to lay out this protocol ahead of time, like in java. But sometimes you want to or need to anyway. > Not at all. Self is an indication that my suggestion will work: I know next to nothing about self, so I'm speculating here. What you describe sounds like a slightly different kind of type inferencing -- profiler-driven inductive inferencing rather than static deductive inferencing. I can see that this could be a big performance gain. But it only tells you which types a variable is likely to have, and does not reduce the total number of types that are possible. I's think that the run-time consequence is likely to be more type checking than is necessary in a statically typed program. Note that static typing can help optimize this type of profiler-driven optimization just a little bit more, by eliminating some of those checks. The difference in performance may not be that great, tho. As far as the fancy IDEs, I'd personally be a bit more interested in knowing the total possible set of types, rather than the set of likely types. This is especially true of my favorite IDE feature: I like to be able to point to an identifier in my code and find its definition(s) or the other references to it. This has been enourmously useful to me when writing C in the past. Autocompletion just gets in my way... but both require a similar level of type information about the program. Haskell, from the little I have read about it, has a very unique approach to optimization. While successful, it seems like it's tied to Haskell's functional nature. > Static typing doesn't increase safety, ability to be analysed, or > anything else -- that doesn't help the compiler of said statically > typed language. There are side benefits from IDEs that have been As a prospective writer of tools that need to analyse ruby, I can tell you that prospects look a lot brighter with a bit of static type information to help out. If you really think it's so easy, then tell me how you would do it. Really, I would love to read a monograph on how you do, say, find references/find definition in ruby, without any help or hints or declarations of variable types. Remember, you have to handle method_missing, too. A given definition of method_missing has to be found as a definition if it can be called from that particular point in the program, but not if it can't be called. > at what point in a class definition can you conclusively say that it is > -- or is not -- an Enumerable? Here you get at the big problem with variable type declaration in ruby. If types are defined in terms of on object's capabilites, then an object's type can change at runtime, as methods are added or removed in the object's class. The key to resolving this dilemma is to notice that changing a method implementation at runtime doesn't alter the type in this sense. So, assignments of values to variables where the declared type is incompatible with the value should be allowed, if it looks like the value might acquire the correct duck type before it's actually used. Likewise, if a value has a correct type currently, but looks like it might lose that type, it's still ok. We have to assume that the programmer knows what he is doing in these cases and that by the time the value is used (ie when we invoke a declared capability that might be present, but isn't guaranteed) everything would be ok. The detection of these 'might' values should be straightforward: they're the values that might be the subject of 'class<