> I agree that a strong type system might make some things harder, but I think that’s because you’re design probably requires a bit more thought up front.
Unfortunately it is rather because, as of now, all but the most advanced & academic type-systems are incapable of solving even basic tasks without getting into the way.
Easy examples:
Pick a statically typed language of your choice and implement a function, that takes any function A that returns a boolean and returns a new function that is just like A but returns the opposite result.
E.g.:
fun even(input: Number): Boolean = ...
even(2) // true
fun opposite(function: ?): ? = ...
fun odd = opposite(even)
odd(2) // false
What are the types of the two question-marks?
Or:
Try to write a function that executes SQL and, based on the SQL, returns a proper return type that contains type-information about what will be returned.
Then write a function that can model joins of two of these SQLs. I.e. the compiler must be able to calculate a new type structure, based on two other type structures.
Or:
Implement a function that "slides" over a list and the sliding size window can be configured.
E.g.:
fun sliding(size: Number, list: List[String], function: ? => ...) = ...
list = List("a", "b", "c", "d", "e")
sliding(2, list, (window: ?) => print(window))
// prints (a, b) then (b, c) then (c, d) ...
sliding(3, list, (window: ?) => print(window))
// prints (a, b, c) then (b, c, d) then (c, d, e) ...
What is the type of the value that will be passed to function: ? => ...
The type must depend on the given window-size. But almost all type-system fail to make this possible. Only so called value-dependent type-systems can do that, and some other's that have built-in special features for specific use-cases, but fail to generalize them (e.g. looking at you typescript).