> dynamic languages don't require you to "emulate the compiler" in your head
On the contrary. Your ‘functional’ timer function takes an argument and applies the - operator to it.
You must be sure, every time you call the function, that you pass an argument to which - can be applied. If you don't, your user might see a TypeError. So you, the human author of the code, must ‘emulate the compiler’ by performing type inference and type checking in your head.
In my statically-typed language, the compiler ensures that my program only ever passes arguments for which the - operator is applicable. I don't need to ‘emulate the compiler’, because I'm actually using a compiler.
I wouldn't do the type checking in my head, I'd just assume it's going to work as intended. If that's not the case, then an error will happen. What's wrong with the user seeing an error? There's always a chance my program will have a bug and fail. So I have to handle that case no matter what and properly deal with unexpected errors, error reporting, etc.
Sure, you always have to handle the case that an unexpected error occurs, but every unexpected error that occurs in production has a cost. What that cost is depends on what your software does and how your users are using it, but there's always a cost. You may decide in your particular context that there is no amount or severity of errors that is too costly for your users to encounter, but most successful software endeavours have some limits.
Bugs are inevitable but we should strive to have as few as possible. Your program not working right is obviously bad UX (as we've all experienced using buggy software).
The reason we have type checking is same reason we have other tooling in our editors to do static analysis like highlighting syntax errors and linting: catching bugs during development is better than finding them in production.
Usually this is "solved" by creating unit tests to emulate what a compiler in a statically typed language would otherwise do. Which is (another) reason that dynamic languages shouldn't be the choice for anything but scripting.
On the contrary. Your ‘functional’ timer function takes an argument and applies the - operator to it.
You must be sure, every time you call the function, that you pass an argument to which - can be applied. If you don't, your user might see a TypeError. So you, the human author of the code, must ‘emulate the compiler’ by performing type inference and type checking in your head.
In my statically-typed language, the compiler ensures that my program only ever passes arguments for which the - operator is applicable. I don't need to ‘emulate the compiler’, because I'm actually using a compiler.