You can also read this article on Medium.
It's always funny when we see how programming languages evolve over time.
One upon a time, when I started my journey in the software development world, dynamic languages such as Python, PHP and JavaScript were appreciated for their flexibility and concise syntax suited for rapid development.
However, as these weakly typed languages evolve, they incorporate features of strongly typed languages, making them closely similar to C++ and Java:
- Python: Type hinting capabilities introduced since version 3.5 in 2015, and enhanced in version 3.12 on 2022.
- PHP: Declared types introduced in version 7 in 2015.
- JavaScript: Extended by the release of TypeScript in 2012 defined as "JavaScript with syntax for types".
Why this shift ?
In strict-typing languages, we explicitly define the types of variables in our code. The goal is to catch the errors during the development phase before executing the program, and provide a hint to the compiler about the memory size to allocate to these variables.
// C++ example: 'y' will be an integer
float x = 3.14;
int y = x; // y = 3 (ignored the decimal part of the number)
On the other hand, dynamically typed languages such as Python, PHP, and JavaScript allow us to create variables and let the interpreter imply their type during the runtime:
# In python and PHP: 'y' will take the same type as 'x'
x = 3.14
y = x // y = 3.14 (float)
How explicit-typing is introduced in dynamic languages ?
In the following example, we declare the same function using dynamic and static typing.
Python:
# using the classic syntax:
def add(x, y):
return x + y
# using explicit typing:
def add(x: int, y:int) -> int:
return x + y
JavaScript / TypeScript:
// using the classic syntax
function add(x, y) {
return x + y;
}
// using explicit typing
function add(x: number, y: number): number {
return x + y;
}
PHP:
// using the classic syntax:
function add($x, $y) {
return $x + $y;
}
// using explicit typing:
function add(int $x, int $y): int {
return $x + $y;
}
PHP 8.2 (released in December 2022) push it further by introducing the support for null, true and false as stand-alone types:
public null $nil = null;
public false $false = false;`
Where is the irony ?
Don’t take this article as an objection to these new features, I do acknowledge the advantages of using strictly typed languages. However, using type annotations in Python, for example, doesn’t stop you from changing the types of your variables:
x: int = 0
x = "John"
print(type(x)) # <class 'str'>
Same for PHP, it will only print a Deprecated warning on the console.
One might ask why the interpreter allows us to execute this code then?
That’s because these languages are built that way: they are dynamically typed by definition. If we remove this characteristic, they won’t be dynamic anymore; they will become strictly typed languages like C++, but slower.
Hopefully, you can ask your interpreter to be more rigid by setting strict_types to true in your PHP file:
declare(strict_types=1);
While in python, you can use the 'mypy' package to analyze your code and catch the bugs:
$ mypy program.py
error: Incompatible types in assignment (expression has type "str", variable has type "int") [assignment]
You can see 'mypy' as an advisor telling you what you did wrong, but it doesn't stop you from executing your code at your risk.
Even if you’re not sure about the type of your variable, you can still use the union operator to reduce the list of accepted types:
The following examples from PHP and Python show how to do it:
y: int | float = f(x) # introduced in Python 3.10
int | float $y = f($x) // introduced in PHP 8.0
let y: number | string // typescript
Are we sacrificing code readability?
Ten years ago, I decided to use Python for my PhD because of its simplicity and the ability to prototype new ideas quickly. Then I started to use it also for my other projects.
Now, I find myself reading some weird PEPs and questioning myself if it’s really worth it to complicate my codebase by including these new features.
Let’s look at an example function that prints the items of a dictionary. Here’s the initial version:
def print_attributes(**kwargs):
for key, value in kwargs.items():
print(key, value)
person = {"name": "John", "height": 1.84}
print_attributes(**person)
By using the recommendations from PEP 692 introduced in Python 3.12, the code becomes:
from typing import TypedDict, Unpack
class Person(TypedDict): # create a class inheriting from TypedDict
name: str
height: float
def print_attributes(**kwargs: Unpack[Person]) -> None: # use the Unpack operator
for key, value in kwargs.items():
print(key, value)
person: Person = {"name": "John", "height": 1.84} # create an instance of the class
print_attributes(**person)
In summary: we created a class that inherits from TypedDict, specified the name and type of each item, and used the Unpack operator to tell “mypy” that the received object is a TypedDict.
As a result, our code doubled in size. It would become even longer if our object had more items.
Fortunately, we can use static typing for some parts of our code and leave the rest as dynamic. Or we can choose not to use it at all if we prefer.
When should we use it?
Don’t feel pressured to rewrite your entire codebase just because you learned a new, shiny feature.
These new features are like tools. My advice is to use them wisely:
Use static typing in the following scenarios:
- When retrieving data from external sources, such as databases, libraries, and APIs.
- In critical parts of your code where failure is not allowed.
- When your codebase is prone to frequent bugs.
Avoid using static typing when you are:
- Designing a prototype to quickly test your idea.
- Implementing internal logic where type checking will only result in verbose code with and no benefits.
- Just displaying data on the screen (e.g. plotting charts, images, numbers…).
- Writing a command line script with no user inputs.
Keep in mind that when it comes to coding, the golden rule is always to strive for simplicity, except if you have a good reason to complicate things.
Top comments (5)
I was always bothered by static typed languages because you are forced to use types for everything. For example in c# and java you have to overload methods when you call them with different parameter types or more or less parameters.
Maybe you could add an extra benefit of static typing, documentation. I don't see typing not as a complication, more as a way to make the code easier to understand.
for example
fn ($a, $b) => $a + $b;
If you type hint the parameters with int you understand it is used to sum the parameters. If you type hint them with array you understand it merges the arrays.I love the way how it is added to dynamic languages. You are in control of the level of type hinting that is required for your project. And you can ramp up or down the level for different parts.
If you look at rust and go, and other modern/newer languages, they are using type inference which is as I understand it making the typing looser for a heavily typed language. Dynamic languages come from another direction, but have the same functionality than the modern languages. That is a win in my book.
I cannot agree more with what you said: "I love the way how it is added to dynamic languages. You are in control of the level of type hinting that is required for your project."
As example I was once doing buch of mathematical calculations where I know my variables are all float numbers and will stay floats as it implies mostly multiplications and no calls to external libraries, why bother myself with typing it these kind of situations for example.
The only thing I have a problem with in your comment is "I know" and " why bother myself"
I assume all my code will be read by others. That is why I think typing is a part of the code documentation.
It is the same as using Interfaces. Interfaces on their own do nothing, but they are the design of the class. Which makes them very powerful.
Putting it that way, it makes sense, yes.
The situation I'm sharing should not be generalized; the code was shared only with some researchers that knew exactly what these mathematical equations expect as entry and what result would be returned.
great article, i totally agree with you