: If you're asking about Java specifically in the context of Smalltalk, then the main difference is the static type checking. A big section of the industry believes static, manifest types are desirable for software engineering. -- DaveHarris
for a definition of the term.)
See also ManifestTypingConsideredGood
, at least for some there's no when -- it's simply a good thing.
by many in the industry? Any theories?
There seem to be answers to two questions here - "Why might it be good to use a language that forces manifest types everywhere?", and "Why might you give a declaration a manifest type in a language where they are optional?"
- My theory is that it catches the sorts of bugs that managers can understand. Unfortunately, they're also the most trivial bugs to find using good UnitTest. The price you pay is difficult refactoring... not a good trade-off. -- AlainPicard
- I used to be an advocate of static typing, and couldn't understand why smalltakers didn't like it until I learned about XP. Certainly, refactoring is much easier with dynamic types. But on the other hand, Java/C/C++ programmers rely on the compiler type checking to validate their refactorings so they don't need to write unit tests so often. -- AndrewSemprebon
- Actually static typing enables automatic refactoring, because the refactoring tool is given all the context for which to work on. You just have to try out something like IntellijIdea if you claim that static typing makes refactoring difficult. The reality is the opposite: static typing makes it trivial and automatic, and your code can't break because of it.
- So your saying that in IntellijIdea, if I change the return type of a method that is called 30 functions deep in the call stack, it will fix all those 30 functions for me?
- If the interface stays the same, the return type doesn't have to be changed and it's a change of a single line. If the interface changes, however, all bets are off. Then you can't call the same methods for the returned object or pass it on to the same functions as previously. So it would require changing code in a lot of places, and something like that can't be automated in any language. Fortunately a good IDE for a statically typed language can show red markers on all lines that must be changed for the program to function correctly. Is that good enough?
- When a type declaration is specifying an actual requirement (or is traceable to one), rather than being inserted solely to satisfy the compiler.
- When a function is otherwise only intended to work with a specific type, and you want to ensure that other "versions" of the function are excluded. Latent typing (dynamic or static) essentially makes every function a template; however it is often the case that providing anything other than the intended type will compile/run (no type errors or DoesNotUnderstand) but will produce useless or incorrect results.
There was a religious war about this in comp.lang.functional recently, so you can find more info at dejanews too. -- LukeGorrie
once said something along the lines of not liking static typing because it made him say things before he knew they were true. I think that its advantages come from being able to say those same things when they become true. -- PhilGoodwin
Great comment, Phil, but circular. In a rigid (frigid?) sense, these things become true only when you say them - assuming your compiler and run-time system are working. Kent's original has the same flaw.
- Err, no. 1 is an integer even when I don't say so. The question is: will an integer be good enough in this position forever? Or is there a good chance that I will want to use a different type in the future? In the former case, it's nice to be able to have the type checker find any code that tries to put something other than an integer in that place. In the latter case, it's nice to be able to change the type without having to change a lot of code.
But like so much else that is said about software, both statements are not only strictly wrong but also very useful approximations to the truth. The issue we're grappling with the whole time is the complex evolving boundary between the "informal and unbounded" real world and our even more fuzzy human intentions to change it (on the one hand) and our often feeble attempts to describe working, formal systems to fulfil (at least one or two of) those human intentions (on the other hand). See RequirementsAndDesign
I'd still vote against manifest typing probably but you've captured for me that other old feeling in Smalltalk and other untyped systems: why can't I declare my "knowledge" or "intention" for this one variable if I jolly well judge that it's highly unlikely ever to be other than an Integer (say)? And to declare it in a way that not only helps other programmers to understand but also enables the compiler, run-time system, database query optimizer etc to do their intended stuff a little more efficiently than otherwise? Why am I prevented from making this choice? How is denying even this possibility compatible with evolutionary approaches which acknowledge at every level that some things are (or must be made to be) better known than others, otherwise we can make no progress at all?
You will probably be surprised at the number of times future work shows your assumptions about the type of the variable to be incorrect. I've seen some very nifty program improvements where someone said, "Hey, this doesn't need to be just a String, in fact it can be anything that supports printing itself as a string!" The first programmer (would have) made a blooper by declaring that variable as "only a" String. -- Alistair
I agree with this on many occasions in practice. I'm asking though whether the "important discipline of not typing any variable" (funny how you can reverse almost any old software aphorism on Wiki and become a hero!) needs to be "imposed" by the language. I honestly don't know. -- rd
Actually, TypeInference can sometimes help make this sort of discovery. Working with quite an empty little mind, the type inferencer discovers a type approaching the minimal restrictions for a piece of code to work, without incorporating the preconceptions of the author.
does have optional type declarations/assertions: (the 'integer foo) or some such.
Is there any benefit to be had in finding type errors ahead of run time or in optimizing implementation? How often do keen common Lispers use it?
It is mostly used when the writer knows
that the types can only have one possible value (e.g. in heavy numerical work) and gives the compiler a chance to represent the underlying values directly, rather than boxing them into lisp objects which know there own types. If you compile at low safety, and violate the passing convention, you're in trouble (having voluntarily declined to accept Lisp's verification of your type assertions). -- ap
In the number-crunching communities (like image processing and numerical computation), by far the biggest benefit of static (manifest) typing is execution speed - statically typed code can be much better optimized, since the compiler knows so much more about the program. A statically typed version can easily be a hundred
times faster than it's dynamically typed counterpart. This difference is quite significant if you have to process 20 Megabytes of data every second.
are increasingly used to get the best of both worlds. Using, for example, the template mechanism of C++, code is written in terms of type variables (identifiers representing a set of possible types rather than only one type), so that we need not know the exact type of the objects involved. Automatic type inference is done at compile time
, so that the speed of static typing is not lost. I consider this a very good compromise between static and dynamic typing at the level of "workhorse functionality". -- UlliKoethe?
- Of course, that's basically an admission that manifest typing is not so valuable, after all.
What about making strong typing optional? A bit like the variant type in VB?
has this in a form. There is a static type checker called MrSpidey
, that tries to statically determine all the types in the program and flags possible errors. It is then the programmer's call if its really an error or a valid program that can't be statically typed. This approach is known as SoftTyping
"Hey, this doesn't need to be just a String, in fact it can be anything that supports printing itself as a string!"
- sometimes what needs to be implicit is not the types but the relationships between the types. You can have statically checked type systems where the subtype relationship is inferred by the compiler, eg by matching method signatures. This means you can be a subtype of some type you have never heard of. It gives you a lot of flexibility.
This is part of what Smalltalk does, of course. It divorces types from the class hierarchy. Quite a lot of the damage of a static type system is caused by the type hierarchy being too rigid. Especially in having to know in advance what types you must conform to. -- DaveHarris
This what TypeClasses
do in HaskellLanguage
I consider manifest typing if it lurks through interfaces a bad idea
. As with many things, an optimum lies between dynamically untyped
languages and type rigor. Often, rigorous typing of interfaces (especially regarding simple types or simple structured types (records)) results in enormous code bloat at the client side using such an interface. If its just about data often an AnyThing
is what served us very well. OTOH I still like to declare what kind of abstraction I intend to pass or get as a parameter. I consider all the type safety stuff deriving from the time when FortranLanguage
didn't check types and the then dumb compilers had no idea of type inference. -- PeterSommerlad
Manifest typing is just a compile-time unit test. If you are doing a mathematical calculation on your inputs, you want to ASSERT that your inputs are numbers. Declaring them to be integers is your unit test. If you pass in a boolean your unit test fails. Sure, you lose the flexibility to call SQRT(x) on a boolean (because you defined SQRT in some impossibly arcane manner to eek out every last drop of flexibility imaginable), but at what cost?
I say, take a dynamic, strongly typed language, and build in all your static type checks as ASSERTS. Voila, problem solved...
How many times have you used a CeePlusPlus
library in which the only
documentation was the types of the parameters? It is not a good thing to encounter, but in my own experience I have encountered these undocumented libraries quite a few times. Often I was able to eek my way through using nothing but the types of the variables, which the author COULDN'T leave off because CeePlusPlus
are both statically typed.
Thinking about this, I would like to draw an analogy. I think that if your types are well-designed (note the caveat), then using a statically typed system provides a sort of DesignByContract
. If you are not familiar with this, it's a concept pioneered by EiffelLanguage
, which says basically (this is my own formulation):
- Each function (aka method, aka procedure, aka routine) should specify certain "Preconditions" and other "Postconditions". Essentially, the function is guaranteed by its caller that all of the preconditions will be true before the method is invoked, and then if so, the function guarantees to its caller that the postconditions will be true AFTER the call. A precondition might be "name != null", and a postcondition might be "result[i] > result[j] for all i and j where 0 <= i <= j <= result.length".
- The big advantage of DesignByContract as implemented in EiffelLanguage is not just that the responsibilities of the methods are clearly defined (so that not EVERY method needs to check to be sure "length != 0"), but that the compiler can be asked to VERIFY these assertions. It may seem wasteful to (as in the example above) verify that a list is in order right after sorting it... this is the sort of thing that wastes time and slows the program. But in Eiffel, you can turn the tests ON (for development/debugging) or OFF (for performance, in delivered code).
Well, if you'll accept that DesignByContract
, as formulated here, is a great idea, then I would like to argue that the use of static types is a (limited) form of DesignByContract
. If I declare that the 2nd parameter to f() will be of type "SortedDictionary?
", then I am in effect introducing a precondition. The compiler is asked to verify at compile time
that I have, in fact, satisfied this precondition. In fact, it does this using the same sort of rules that an automated correctness prover would use.
Now, this won't work if I declare my variable to be of type "Character", but then write a method that balks if passed a non-ascii character. It won't work if I declare all of my parameters to be of type "int" instead of defining types for the appropriate sub-ranges. It won't work well if I declare my parameter to be of type "List" when all I REALLY needed was any type supporting the "getEnumeration()" method, because someday I'll want to pass a "WindowCollection?
" to the method and it won't work because that's not descended from "List". So it only works if your types are well-designed
Also this is in no way as powerful as full-featured DesignByContract
. There are many kinds of preconditions, postconditions, and invariants that you would like to be able to require which aren't equivalent to some statement about the types of the values involved. But the point that I'm trying to make here (and I apologize for fumbling around loquaciously in the process) is that several people have tried, on this page, to claim that there is NO GOOD REASON for using static typing. I feel that the compiler-enforced contract about what may be passed in that parameter or placed in that variable can be made into a very useful tool. -- MichaelChermside
PS: See also TypesAreContracts
And also read up on DependentTypes
Incidentally, it's worth distinguishing between manifest typing (where the types are declared explicitly in the code) and static typing (which is more general and allows for, eg, type inference). We could rephrase the question as, When are explicit static type declarations better than inferred static types?
A problem with inferred types is that you can change the types without realizing it. In effect, the inferred type leaks implementation detail. It tells clients which methods were actually used. Dynamic typing has the same problem.
When we explicitly declare an argument to be of type STACK, say, we are reserving the right to call the push, pop and isEmpty methods. This is independent of whether we do actually call all of them. We may only need isEmpty, but callers cannot take advantage of that. We can change the implementation to call push and pop later, and be sure of not breaking any client code. Thus manifest types can result in looser coupling. Their pessimism creates wriggle-room.
The benefit is most at subsystem boundaries - at the interfaces which refactorings cannot easily cross. With CollectiveCodeOwnership
, that means the interfaces between teams.
Note that all language that do TypeInference
also allow (but don't require) you to manifestly type interfaces. So the interfaces between teams can be typed manifestly, and the rest can be inferred by the system.
One advantage of ManifestTyping
is that it helps automatic refactoring tools. Automatic refactoring tools for dynamically typed languages require a lot more manual input.
allows tagless GarbageCollection
. That is, a suitably expressive type system such as that of ML or Haskell allows a garbage collector to detect garbage without the need for additional runtime information. From the GC FAQ (http://www.iecc.com/gclist/GC-faq.html
It seems like you misunderstand: MlLanguage and HaskellLanguage don't force ManifestTyping. They have strong, static typing that allows the efficient TaglessGarbageCollection. ManifestTyping has nothing to do with it.
- ...in a statically typed language with a type system as complex as ML's, it is not necessary to tag pointers to make a garbage collector work. Goldberg showed something much more interesting - that you could not reconstruct the types of all reachable objects (without auxiliary information maintained at run time). However, and this is the fascinating part, these objects were guaranteed to be "semantic" garbage - i.e., even though the objects were reachable, they would never be accessed.
Manifest typing lets a good IDE, like Eclipse, speed up problem detection enormously. If I misspell a variable or method name, Eclipse displays the error immediately, letting me fix typos in real time. The IDE detects many problems while I'm writing the code without waiting for a compilation / testing cycle.
This doesn't have much to do with types. SqueakSmalltalk catches most typos for me, too, simply by telling me whenever it sees an identifier that it hasn't seen before. (You can certainly do better than this when you've got a type-checker, but I find that most typos do fall into this easily-caught category.)
The problem with ManifestTyping
is when it violates OnceAndOnlyOnce
Blah x; // In many cases, x never leaves the scope, so you can do this.
Blah x(12); //Or, equivalently, Blah x = 12;
auto x = new Blah(); //If it does leave the scope, we must do this. Not so bad, is it?
auto x = new Blah(12);
In C# (3.0 or later, I think):
var x = new Blah();
var x = new Blah(12);
Blah x = new Blah(); //All the way to 2.0, and still no optional type inference.
Blah x = new Blah(12);//Bleah.
This is more of a problem when you have longer types:
ArrayList<SpriteObject?> x = new ArrayList<SpriteObject?>(); //AARRGH!!!
for the opposite question.)