Pushing "ifs" up has the downside that the preconditions and postconditions are no longer directly visible in the definition of a function, and must then be checked at each call site. In bigger projects with multiple contributors, such functions could end up getting reused outside their intended context. The result is bugs.
One solution is some kind of contract framework, but then you end up rewriting the conditions twice, once in the contract and once in the code. The same is true with dependent types.
One idea I haven't seen before is the idea of tagging regions of code as being part of some particular context, and defining functions that can only be called from that context.
Hypothetically in Python you could write:
@requires_context("VALIDATED_XY")
def do_something(x, y):
...
@contextmanager
def validated_xy(x, y):
if abs(x) < 1 and abs(y) < 1:
with context("VALIDATED_XY"):
yield x, y
else:
raise ValueError("out of bounds")
with validated_xy(0.5, 0.5) as x_safe, y_safe:
do_something(x_safe, y_safe)
# Error!
do_something(0.5, 0.5)
The language runtime has no knowledge of what the context actually means, but with appropriate tools (and testing), we could design our programs to only establish the desired context when a certain condition is met.
You could enforce this at the type level in a language like Haskell using something like the identity monad.
But even if it's not enforced at the type level, it could be an interesting way to protect "unsafe" regions of code.
One solution is some kind of contract framework, but then you end up rewriting the conditions twice, once in the contract and once in the code. The same is true with dependent types.
One idea I haven't seen before is the idea of tagging regions of code as being part of some particular context, and defining functions that can only be called from that context.
Hypothetically in Python you could write:
The language runtime has no knowledge of what the context actually means, but with appropriate tools (and testing), we could design our programs to only establish the desired context when a certain condition is met.You could enforce this at the type level in a language like Haskell using something like the identity monad.
But even if it's not enforced at the type level, it could be an interesting way to protect "unsafe" regions of code.