When Is Manifest Typing Considereda Good Thing

From LanguagePissingMatch: 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

(See ManifestTyping for a definition of the term.)

See also ManifestTypingConsideredGood, at least for some there's no when -- it's simply a good thing.

So: WhenIsManifestTypingConsideredaGoodThing 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?"

There was a religious war about this in comp.lang.functional recently, so you can find more info at dejanews too. -- LukeGorrie

KentBeck 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.

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?

-- RichardDrake

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

See StringVariablesConsideredHarmful -- FalkBruegmann

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.

CommonLisp 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.

Recently, TypeInference and CodeGeneration 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?

What about making strong typing optional? A bit like the variant type in VB?

-- GlenStampoultzis

DrScheme 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, right?

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...

... right?

-- FelixTan?

How many times have you used a CeePlusPlus or JavaLanguage 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 and JavaLanguage 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.

ManifestTyping 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):

...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.

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.

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. In C++:
    Blah x;     // In many cases, x never leaves the scope, so you can do this.
    Blah x(12); //Or, equivalently, Blah x = 12;
In C++0x:
    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);
In Java:
    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!!!

(See BenefitsOfDynamicTyping for the opposite question.)

CategoryCodingConventions CategoryLanguageTyping

View edit of August 24, 2010 or FindPage with title or text search