Type Systems
The page you link to is about type systems. The differences most people should be aware of when starting out is static vs dynamic typing and strong vs weak typing (though feel free to explore and ask about more).
Type systems are logical rules built into compilers and interpreters for assigning types (indicative of the kind of data something holds) to each term (variables, expressions, functions, modules) in a program, i.e. identify something as 'a list of ints' or 'an array of bools' or 'an array of arrays of chars'.
Strong vs Weak Typing
Strong vs weak typing are relatively easy to understand, though these are sometimes used a bit loosely. I think these terms are best used to describe individual features than languages as a whole, though you can certainly argue, for instance, that some languages are more strongly typed than others.
Strong typing features include stricter type-checking at compile time or run time, checking the types at assignments, returns, function calls and so on to look for possible violations, and minimal implicit type conversions (except possibly those that lose no data, e.g. an int to a float). Weak typing features include the opposite. The extreme example is assembly, which is technically typeless - the concept of types simply doesn't exist. Another example is liberal type conversions, including those that may lose some information. A rather interesting example is pointer arithmetic in a language like C, which makes it easy to get around the type system. C++, for instance, requires each declaration to explicitly carry a type, so you can only have something like int x
or double y
or bool* f
. In the line you quote, the page explicitly compares templatised code (roughly, generic code that can be reused with different types). In C++, that requires explicit type specification when a templatised function is called to guide the compiler.
// templatised code
template <typename T>
T add(T a, T b) {
return a + b;
}
// assuming everything is declared, call as
sumD = add<double>(3.1415, 1.4489);
/*
// The compiler generates and adds
double add(double a, double b) {
return a + b;
}
*/
sumI = add<int>(7, 2);
/*
// The compiler generates and adds
int add(int a, int b) {
return a + b;
}
*/
Python, by comparison, uses 'duck typing' - 'if it walks like a duck and quacks like a duck, it must be a duck'. In Python terms, an object may be used in any context that it supports. This makes template code much easier to write, since you don't have to specify the type you're passing at every call.
Static vs Dynamic Type Checking
Type checking can be done either statically (at compile time) and dynamically (at run time). Static and dynamic type checking is one example of static and dynamic analysis, the former using the source code, and the latter using the state at runtime. Unlike strong and weak typing, these two are not 'somewhat subjective'.
There are tradeoffs to which a language implements. Static type checking, for instance, allows the compiler to make extra optimisations and allows predicting some errors based on the source code alone, though at the cost of more boilerplate and sometimes complex error messages. Dynamic typing allows more polymorphism, at the cost of possibly more runtime errors and therefore more testing and maintenance, besides prohibiting some optimisations that static typing would allow.
Most languages that use static type checking combine it with some form of dynamic type checking as well to catch runtime errors or cover features such as downcasting (casting a base class pointer to one of its derived classes), which are impossible to statically type check.