As noted in StrictTypingIsaTest, it is worth considering StaticTypeSafety and UnitTests as closely related tools, two approaches that mesh together. They are not just different approaches. UnitTests are general and flexible. StaticTypeSafety just breaks out one part of what you can do with UnitTests and puts it on a formal basis.
There are countless analogies for this. In AssemblyLanguage, you can implement loops by jumping back to the start of a section of code until some register contains the value zero. Along with subtraction, this lets you build the equivalent of for loops, while loops, do loops, and any other kind of loop you could think of. So it's very powerful. But if you've got high-level alternatives, it's better to use them, because there's less donkey-work that way, and less chance to make a mistake. It's just a kind of reuse.
StaticTypeSafety is like the while loop. Hand-coded UnitTests are like jump-if-not-zero instruction. Therefore, you can survive without StaticTypeSafety because you can implement the same things with UnitTests. But if you have StaticTypeSafety in your environment, you may as well use it, because it will save you writing and maintaining a specific subset (see below) of your test code. But it will still leave you with stuff that requires UnitTests to be designed and implemented.
OH MY (insert deity of your choice here.) !!! This is the single most coherent comparison of the two methodology camps I've ever seen. Thank you!
Why Is StaticTypeSafety Deliberately More Limited?
Suppose that there is an integer in my program:
int nMilesPerHour; // Must always have a value between 0 and 55Fact: It is impossible to construct an algorithm that can (given finite time) analyze any arbitrary program and ensure that the condition in the comment is always met. (It comes from the the HaltingProblem. To put it simply, some programs just have to be executed in order to check such things, and some will neither terminate nor break the condition, so the compiler would hang up whilst attempting to check them! Related to GoedelsIncompletenessTheorem.)
TYPE MilesPerHour? = [0..55]; VAR milesPerHour : MilesPerHour?;The nice thing about this is that you (and your compiler) can throw away bounds checking. You can say things like:
TYPE WorkDays? = [Monday, Tuesday, Wednesday, Thursday, Friday]; VAR dailyResults : ARRAY WorkDays? OF Result;Whilst such languages do allow you to specify the bounds, they do not guarantee to test them at compile-time. For example, does the assignment
milesPerHour = milesPerHour + 7;violate the bounds? To decide this, we'd need to know the precondition. Even a language like Eiffel does not allow you to specify sufficiently powerful pre/post conditions to permit static analysis in all cases.
So instead of static analysis, the compiler inserts run-time assertions. You hope that these will never be triggered, but can you be sure? Well, you can't be certain; but you can write UnitTests to hit the boundary conditions. If you have post-conditions in your contracts then the unit tests will not need to check the results: they only need to stimulate the interesting input patterns.
''By using an integer instead of an object that encaps the idea of miles per hour you have deliberately said any integer value is ok and any integer operation is ok. If you don't want that then don't be lazy and make a real abstraction. Just this simple approach could have solved the Arianne-5 disaster.''
[True, but that's beside the point of the example. In the RealWorld, one wouldn't (or shouldn't) use an int to represent a continuous physical parameter like miles per gallon anyway; a float is the most appropriate datatype for such quantities in most languages. Depending on how much precision you want to give to the milesPerGallon parameter]
No, a MilesPerHour? object the best representation because that can enforce semantics. If a float is used internally it hardly matters. Using a MilesPerHour? object allows the compiler to make sure it is used being properly. Runtime checks add another level of checking.
[Agreed, which is what I meant: A discrete unit type to represent the quantity; with a more appropriate internal representation (i.e. not an integer).]
Two Aspects of UnitTests
We can consider two aspects of unit tests:
So why do that part yourself, manually, every time? Why not get the computer to do that part for you?
Automatically Generated UnitTests
To make this as clear as mustard, let's try to answer the question, what parts of typical UnitTests can we invent a tool to automatic generate?
Suppose we were working in a non-typesafe language that let us define classes like this:
class StairMaster { public void goUp(); public void goDown(); public boolean isUpstairs(); };A reasonably pure interface test for this class would be like this:
try { StairMaster sm; sm.goUp() sm.goDown() bool junk = sm.isUpstairs(); } catch(MethodCallException e) { // report failure! } catch(...) { // ignore all other exceptions (this is a pure interface test) }All this test does is attempt to call the methods that should be there. It isn't interested in what effect they have on the state of the object (for example, we don't check that junk is false.)
So this test can be used to verify if another class is polymorphically compatible with StairMaster?, and so can be used by any StairMaster? clients.
Of course, this unit test can be automatically generated by a tool that reads the class declaration. That's all StaticTypeSafety is. It saves you hand coding those parts of your UnitTests.
What you described here is hardly enough of an UnitTest to replace static type checks. You miss to specify how you get that reference (not to mention that it is pointless to declare it StairMaster x in an untyped language (you just state something like var x= someFunction( someArguments); ), so you have to call a function (or a constructor, but let's assume a constructor is like any other function), that function can return any reference and maybe based on if branches, the results of other functions returning other untyped references, and so on).
So what you presented here is a very symplistic view, and by far not good enough to replace a type-checking compiler. And I hope you won't try to reinvent the whole type theory by means of unit tests. If you do that, please present it in a formal consistent matter, with references to previous work and research, before you lay any claims that you reinvented the wheel. (- That's all StaticTypeSafety is. - You must be kidding, right?) -- CostinCozianu
You seem to have missed the point of my argument - I'm not preaching in favour of abandoning StaticTypeSafety. I'm doing the exact opposite. I'm totally convinced that type safety is important and I'm (consequently) fascinated by those who (a) manage without it and even (b) claim that this gives them an advantage. This Wiki has a general bias against StaticTypeSafety because it (the Wiki) is firmly part of Smalltalk culture. Hence I'm trying to convince Smalltalkers that there is value in the type systems of Java and (dare I say it?) C++, which personally I'm culturally tied to - as I've said elsewhere, I think in interfaces, not classes. Aside from that, if you look at the writings on this Wiki, I think you'll find very few of them are presented as normative specifications with full references! Perhaps you're applying a high standard here simply because you don't like what (you thought) I was saying? :) -- DanielEarwicker
UnitTests used in this style but beefed up enough to be useful would likely turn out to be design by contract, wouldn't they?
Anyway, how can I automatically generate unit tests when I write the tests first? What would be more useful would be to have a tool that automatically (and without me ever having to see them) generated the class definitions that my tests need to compile today. -- KeithBraithwaite
You can actually do this with QuickCheck, because you only establish properties that must be for all legal inputs, not properties that must be true for a single input. -- ShaeErisson
It is impossible to construct an algorithm that can analyze any arbitrary program and ensure that the condition in the comment is always met.
This is true, but misleading. Most people aren't interested in properties of arbitrary programs, such as generated by the proverbial monkey on the typewriter. There is some research in type systems that are not decidable, which means that your compiler might loop. Which sounds scary, but bear in mind that even compiling C++ programs is undecidable in general (due to possible non-termination of template expansion).
See the Cayenne paper:
http://www.cs.chalmers.se/~augustss/cayenne/paper.ps
More recently, Epigram:
Extending/explaining this. It's obvious that with or without static typing, unit tests are useful. Further, with or without unit tests, static typing is useful, why? Because static typing can replace an infinite number of unit tests, the same way a proof that there are an infinite number of primes replaces the (impossible) task of showing them. A trivial and unconvincing example is (using Haskell):
data A = A data B = B f :: Integer -> Either A Bin a dynamically typed language it would take an infinite set of unit tests to show the simple property (ignoring non-termination), that for all values this function will return either A or B, in a (sound) statically typed language it takes zero. A much less trivial and much more useful example is the use of parametricity in the definition runST (see "Lazy Functional State Threads"). The type disallows one misuse of the library. Furthermore, the misuse is quite subtle and almost certainly would be missed by test suites for a specific application. Yet, with static typing, the fact that your program typechecks is a proof that it doesn't have this bug. (This example is also another example of static typing allowing program optimizations, notable in that it doesn't just get rid of a simple run-time type check. Making a run-time check to catch this misuse would be difficult to implement and difficult to prove correct, and certainly slower.) -- Darius