Re: Well trodden path
Posted: Sep 22, 2005 4:21 AM
> > Really? If these are related by inheritance, there's no
> > problem handling this situation in a statically typed
> > language, such as Java:
> And if they are not related by inheritance, then you can't
> substitute one for another, even if they happen to
> implement the same protocol.
This is a separate argument. You originally said:
> Static typing
> eliminates the need for type testing.
> Until you support inheritance in the language, and then it
I argued that if you have a lot of need for downcasts, your design may have a problem.
What you say above is an argument for "duck typing"; it doesn't have to do with static typing and inheritance, which you started with.
> > I think
> > "duck typing" is very useful; what I meant was that the
> > problem is the lack of _support_ for it, in languages
> > C++, as well as in dynamically typed languages
> Dynamically typed languages usually only implement duck
Yes, but they usually lack support for checking type constraints on function boundaries (which you get with support for "concepts").
> > The problem I speak of is that there's typically _no_
> > checking at function boundaries, when "duck typing" is
> > used.
> Why should you check there? In a duck typing system, you
> are only in trouble when you ask something to quack that
> can't. This is the point of error. Not the function call
If you call a sort function with a an object that doesn't conform to the requirements of the comparator (such as having the less-than operator defined), then you'll get an error message pointing to where the function tries to use the unsupported feature. Normally, you'd then think the problem is in the sort function (since that's where the error message points to), when in reality, the problem is that you're calling it with parameters that don't conform with the requirements. Agree?
If you could specify the "duck typing constraints" on the function declaration, then you could get a type-error at the call site, where the error is.
As mentioned, this - the effect of "duck typing" without support for "concepts", is what can make debugging template-code, _and_ dynamically/undeclared typed code, difficult, because you may get an error message from a very different place than where the error actually is (when you consider that a function or module may have certain requirements on its parameters).
> > Therefore, any type-related problems may manifest
> > themselves as incomprehensive error messages deep into
> > implementation (and in the case of a dynamically typed
> > language, you may not get any error at all, unless you
> > ensure that all possible cases are covered with tests.
> I seldom find the errors caused by type errors cryptic.
> They are usually obvious and straight forward. I also
> o find that we spend way too much time guarding against
> this error considering how often it actually occurs in
I've several years of experience with developing applications in PHP, and my experience (as well as my coworkers), is that type-related errors stand for a large part of our bugs, as well as the difficulty in finding the correct place the error originates.
For this reason, we've developed some "manual" type-checking at function calls, but it's messy, as the language has hardly any support for it. We find now that we catch a lot of errors this way, that might otherwise go undetected, or show up in strange ways. Unfortunately, the codebase has hardly any tests, and that could have helped, but I still think type checking and unit tests complement each other (as I've said in other postings about not having to write lots of otherwise unnecessary tests).
> > As I said, the same problem exists in languages with no
> > type declaration on functions: any errors in the types
> > passed values are only (if even then) reported from the
> > function implementation, not at the call site, where
> > error actually is.
> Yes, but usually you get the stack. For instance, in
> Smalltalk, you get a walkback (debugger) on the program
> that usually shows you right where your error is. These
> problems are easy to find and fix.
Not in our experience. Yes, you may walk the stacktrace, and inspect each function call and parameters up to main(), but it would be much easier if the error message pointe to the actual error (as in the function call, not inside the function).
> > This problem may be eliminated with support for
> > (abstract requirements on types, such as a type
> > the "+" operator, and what type that operator returns,
> > etc.),
> To me, this is just more evidence that duck typing is the
> saner system.
"concept checking" is meant to give "duck typing" similar type-checking adavantage as ordinary types, without giving up the advantages, such as not requiring inheritance to work, etc. I think you may be misunderstanding what "concepts" are about, or we may mean something different with "duck typing".
Besides, what do you find "not sane" of what I described above? This is, as mentioned, also how it works in Haskell, ML, etc. (perhaps also Squeak, from what you describe)
> There is a continual loosening of typing
> constraints going on in c++
I'm not sure what you refer to, here. Ever since templates came, in the 80's, you could call a function template with any type. However, because of the popularity of templates, and the mentioned problems with this "duck typing", work is now underway to try to give "duck typing" type checking, as well, in the form of "concepts". I think this will be one of the next big things, in C++.
> "concepts" sounds suspiciously like "traits"
> which has recently been added to Squeak Smalltalk.
Traits, what the term mean in C++, anyway, is a way to "work around" the lack of support for "concepts" in the language. We've found that you really need language support for this (it can't be done completely as a library, unlike traits, which is a language feature, typically).
> Several other languages are beginning to adopt them as
> s well. Basically they are method bundles that can be
> adapted to arbitrary classes without hierarchical
Then "traits" in Smalltalk appears to be quite different from the C++ use of it. :) And, yes, several languages has this, including, apparently, Squeak. Vesa has shown examples of it in ML, and it aso exists in the form of "type classes" in Haskell.
> > even better, catching the error at
> > the point it exists.
> That's a fairly fuzzy concept I think.
What I meant was like the example with sort(), above. sort() has a strict set of requiremenst on its "comparator" object (including function pointer/reference), that is passed in. If what you pass in doesn't conform to these requirements, then you've violated the interface/contract of sort(), and the violation clearly is in the call to sort(), not in sort() itself. Agree? It's very clear; there's nothing fuzzy about it.
> > 2) It's a completely safe operation: Downcasting in C++
> > using dynamic_cast either succeeds or returns NULL,
> > you may check for, so there's no risk of undefined
> > behaviour, as long as you check the result (it performs
> > run-time check, if necessary).
> Its ugly and verbose.
You complained it was an unsafe operation. I replied with that it's not, unless you count forgetting to check the return value as a risk. However, in modern C++, you typically use something else than a "bare" pointer, that may catch these problems, as well as eliminating the verbosity you show. The need for try-catch may indicate a lack of understanding for good use of RAII (where you typically can avoid that).
> A* a = new B();
> B* b = dynamic_cast<B>(a);
> throw new TypeCastError("Expected B here");
> a.boo(); // throws if boo not implemented on a
> What's the difference apart from the second one gets me
> out of a lot of typing.
In this case, hardly any. But if you pass in "a" to a function; one checks its type, another doesn't, then: plenty.