Call-Trace capability in IDEs
I was thinking about the features that I’d want in a perfect language, and recalled something I was reading about compiler type-checking. I believe that the common type-checking algorithm can be adapted to do checking of interfaces. An IDE should be able to tell you not only the variables and names within the current scope, but also the attributes of those variables (via type inference). We can go farther and do this for functions. Each function can be associated with a return type, and the IDE via syntactical inspection of that function and back-tracking of calls and variables, should be able to tell you the exceptions that might be raised at any point in your code.
This way, you are free to let exceptions propagate to higher levels, and the IDE will keep you informed every step of the way. At the highest level, you should be able to get the IDE to say wether or not any exceptions have gone potentially uncaught or variables unsafely typed. You should also be able to obtain information about where those events occur in the form of a call trace.
For example, if we are editing code in main() then the IDE should be able to alert us that a ZeroDivisionException could propagate up to this level, and that it originated in a function 12 function calls deep (with the call-trace available for inspection) because we were careless when we wrote that subroutine for calculating average last month and forgot to catch the case when N=0.
This places a very heavy burden on the IDE, because it will have to check all possible return situations, and exceptional conditions that can occur at every point in the code. Fortunately, in the case of exception handling, we can think of functional call dependency as a graph. So, for every function A that depends on functions B and C, the exceptions that might get raised at A are any uncaught exceptions raised via calculations within A + exceptions raised in B + exceptions raised in C. Memoization and dynamic programming come to the rescue. Function return types can be similarly evaluated.
Aside: The information that can be gathered at author time might just convert me from my current fanship of dynamic typing languages. Principally, I despise statically typed languages because they are verbose in a terribly uninformative way. All too often I find myself wanting to do the same thing to stuff of different types (like have a container hold both Int and Real). The whole Design Patterns book is really a bunch of techniques to work around the static type system in C++. In an Ideal world each type would correspond to an encapsulated thought, and we’d all be doing Domain Specific Programming with our types. However, this marvelous idea just can’t be implemented with duck-typed languages, due to lack of information at compile-time.
Further Aside: Also, the programmer shouldn’t have to explicitly name the types, it’s the functional interfaces that really matter. Types can be inferred by the language itself. Each variable should be able to hold a range of possible types when the compiler does it’s checking. If the operation is allowed for each type then no errors, otherwise one of the variables had a type incompatible with that operation, and it’s type should automatically be call-traced back to the variables’ instantiation/declaration.
Note: I have no idea how this call-trace idea for program validity will work in the world of meta-programming. But for simple programs this is entirely do-able. If I, as a programmer, can think of almost all the situations each function works under, then the automatic code-inspector should be able to do the same in the time it takes for me, the slow human, to write that function. Partial results can be cached in a file of annotations (like python does with bytecode, and make does with object code).