I don't agree that having a type of a fixed but unspecified size is evil. You can query the sise - you don't have to assume any particular size - and you have explicitly sized types as well.
I suspect we're never going to agree about this but perhaps we can try to understand why the other feels the way they do - so I'd like to briefly (re)state why implicitly sized types disturb me so much. There's many possible ways of classifying different portability problems but one of the most important is: 1) Portability problems that can be mechanically detected. This includes missing libraries/header files, libraries and header files installed in the "wrong" place, missing functions and methods, functions with the wrong type and semantic differences so gross that even the lamest testing will detect the problem. Fixes tend to require conditional compilation. 2) Portability problems that are hard to detect mechanically. This mostly consists of subtle semantic differences that require somewhat carefully designed test cases to detect. Fixes may require conditional compilation but more usually require small subtle changes to the code to ensure that the code doesn't tickle such differences as exist. Usually all that remains is a comment which explains why something is coded in a slightly unnatural way. [If we replace "mechanically detected" with "statically detected", then even gross semantic differences would be in the second category. It makes little overall difference to the argument though.] The first kind is, in some ways, most annoying because it is very visible: the code just won't compile/link until I fix it and the fixes to make it compile remain as an ugly scar on the code. But I feel that the second kind is worse precisely because it is so hard to see: if the code has not been written with those subtle semantic issues in mind it may contain such errors; if the code is modified by someone who didn't consider those issues, it may contain errors. I agree that it is possible to write portable code with implicitly sized types but I am very concerned that it is very easy to write non-portable code containing the second type of portability problem. Using explicitly sized types doesn't avoid all overflow-related errors but at least it makes them appear more consistently and, thanks to the sheer ugliness of the names (Int32, Int8, etc.) it is harder to forget about overflow issues. -- Alastair ps On reflection, I find your example (from last week) of doing radix sorts on Ints a good example of where you would naturally want to be able to do bitwise operations on Ints. I'm not sure if I find it compelling enough to add a Bits Int instance but at least I now know of one example where one might legitimately want to. In the absence of a Bits Int instance I would have to write the code like this: -- this code to go in the same file as the code that assumes that -- Int and Int32 are isomorphic #if sizeof_Int == 32 type Intlike = Int32 toIntlike = intToInt32 -- or fromIntegral fromIntlike = int32ToInt #elif sizeof_Int == 64 type Intlike = Int64 toIntlike = ... fromIntlike = ... #else #warning ... #endif radix_sort :: [Int] -> [Int] ... rest of code converts to/from Intlike every time it does a bitop Yeah it's a bit ugly but at least it makes the assumptions it relies on fairly explicit. Even without a comment (which would be a worthwhile addition), the explicit conversions and the ifdefs make it fairly clear that we're assuming some kind of isomorphism to exist. It is unfortunate that protecting against potential portability issues is unnecessary for radix sort. To some degree we are used to that sort of thing though: there are some perfectly safe Haskell programs that the GHC/Hugs typesystem will not let me write.