3 ms·
The way the post presents things is a little bit confusing. It does go on to present actual validation, which you'd still need to do in most typed languages, li
by geofft 5y ago
The way the post presents things is a little bit confusing. It does go on to present actual validation, which you'd still need to do in most typed languages, like checking for age < 10.
However, despite talking about getting "input from the user," but then it accepts in-language objects and validates them. If it were accepting, say, strings in all cases, e.g.
def validate_age(raw_age: str, errors: List[str]) -> int:
try:
age = int(raw_age)
except ValueError as e:
errors.append(str(e))
if age < 10:
errors.append("age must be at least 10")
return age
then I think the listed approach would make sense. It's not doing isinstance checks - it's doing conversions, which you'd have to do regardless.
But accepting an object that should be an integer, and then checking that it actually is an integer, doesn't seem like input from the user:
def validate_age(age: int, errors: List[str]) -> int:
if not isinstance(age, int)
raise ValueError("Did someone forget to run mypy???")
...
I suppose the approach mentioned in this article would be valid if the input were JSON or something. Then you really would have language-level objects of potentially int, str, or other types, and you'd need to check which particular type you have at runtime, and the function would legitimately be age: Any, not age: int.
Also, if you were really embracing the type system, you'd return a ValidAge object, a newtype around an integer at least 10 (for this application), instead of just an int. That would let you statically make sure that any ValidAge has already been checked. I think you can mostly emulate this in Python with
ValidAge = typing.NewType("ValidAge", int)
def validate_age(raw_age: str, errors: List[str]) -> ValidAge:
...
return ValidAge(age)
except that I don't think Python gives you the privacy features to prevent someone else from constructing a ValidAge.
BTW, your post made me realize why I don't like "types" as runtime programming-by-contract: the whole point of a type system is that it's Turing-incomplete, and therefore you can prove things about it statically (i.e., at/near compile time, not at runtime) without having to actually execute the program. Validation - accepting an unknown input from outside of the program (outside of what's visible to the type checker, which is generally just one program's source code) - is a task you must do at runtime, and therefore is something for whose runtime failure you already want to write error handling. But regular old type checking for proper use of APIs is not, and writing any sort of runtime error handling for its failure always feels weird, because you shouldn't report the error to the user, you should report the error to the developer.