As my programming experience begins to diverse, from traditional command-line programs to web and app development, I have used many programming languages, some of which I like, and some I don’t like. One particular aspect of programming languages, type system, contributes to my feeling significantly.
Data types are a kind of constraint bound to the data, defining what we can and cannot do, and how operations behave. It can be analysed in different dimensions:
Strong vs Weak
0 == "0"
0 == 
"0" != 
0 == []
-1 != true
-1 ? true : false
null != true
null != false
It is impossible to know, from the code, whether the result of comparison behaves as you expect unless you read the documentation. Because the behaviour is brain-damaging, there is an identity operator in both languages, but sometimes they also give different results in different programming language:
The unpredictability of operations between different data types is confusing enough such that I always use the identity operator for comparison, and explicit type casts when operating between types. Even worse, when a weak type system is combined with a dynamic type system:
Static vs Dynamic
A weak type system combined with a dynamic type system, with an incompetent programmer, is like explosive handed to an idiot. It basically makes program analysis impossible because you can’t even know what is the behaviour of operations apart from running the program itself! In particular, if the programmer “takes advantage” of the fact by assigning different types of values to the same variable according to program logic, the whole thing is doomed in a weak type system. It is like taking the express train to hell.
Safe vs Unsafe
In a safe type system, there is no way to circumvent the type system. In an unsafe type system, the language provides ways to circumvent the type system, typically with undefined or implementation-defined behaviour.
An unsafe type system is not necessary evil, but an unsafe type system combined with a weak type system is definitely a way to unnoticeable disaster, like the automatic integer-to-pointer conversion in C.
Explicit vs Implicit
In an explicit type system, the types have to be specified for all identifiers, therefore, if it is initialised with a wrong type, it is caught immediately. However, in an implicit type system, type signatures are optional and types are deduced if not specified. It is an error if the type cannot be deduced. This notion only applies to static type systems because in dynamic type systems, identifiers don’t have types.
I prefer the ability to deduce types because I am lazy and don’t want to type a
VeryLongContainer for a variable which is used only in a few lines.
Nominative vs Structural
In a nominative type system, two types are compatible (i.e. can be replaced with the other) only when declared so, in a structural type system, two types are compatible if their structures are the same (aka duck typing). One of the incompatibility between C and C++ is their behaviours passing
structs. In C, as long as both types satisfy some common criteria, their pointers can be safely casted to each other, which is not true for C++, where proper OOP is encouraged instead.
I prefer to use a nominative type system to prevent accidentally substitute some incompatible type only because it has a method of the same name.