Alas, this still doesn't work because type annotations are advisory and not mandatory. Regardless of the type hints given, I can still call div(1, "surprise") and get a TypeError.
I'd assume that everyone is a consenting adult and if someone really wanted to shoot themselves in the foot there is no way to really prevent that (although mypy does a good job at that). The runtime `TypeError` at the call site of the offending code is actually what I'd prefer over code that mostly works (e.g. `div(1, 2)`) but crashes in the rare event that someone passes `b=0` to `div.`
Worse, because types in Python use subtyping, I can pass an int that does something you don't expect: class MyWeirdInt(int): def __truediv__(self, other): if other == 0: import loging logging.logg("tried to divide by " + other) raise ValueError('divided by zero')
Again there is no general way to prevent people from writing buggy code, the most can do is try and guide people towards using a library correctly (by good naming conventions, suitable levels of abstractions and yes also by making use of type hints)
So your "total function" is still not total: as soon as we move beyond the builtins written in C, literally anything can happen and any promise you make in div can be only advisory, not guaranteed. To get your "total function" you have to catch anything, but that cure is worse than the disease. That means that obvious bugs get suppressed and turned into Nothing() when they should in fact raise.
I fully agree with you that catching any possible exception just to make the compiler happy is the worst of all possible ways to handle errors. This is however not what I suggest; the point is not to make a function *total* in terms of anything that can happen in the interpreter/compile/runtime/hardware - that is what exceptions are for - but in terms of the functions api e.g. dividing by zero being a natural part of division that has to be handled somehow.