Hm, one problem I notice is that if we add a third field, like “occupation”, then we can’t guarantee we get back all useful errors. If we place the occ validator in the original block, then we can have name=Drew, age=90, which would fail validate_drew, but we’d never get there as we’d fail the occ validation. What we’d need to do is assemble the validations as a directed graph, topologically sort it, and then walk the sort and run any validations where all the prereqs are valid.
Unrelated, but an interesting shibboleth here: he defined Valid.apply, not Valid.__call__. With __call__ he could have written a(b) instead of a.apply(b). That tells me he knows Python but isn’t an expert at it, making me wonder if there’s a different approach to declarative validation in Python.
TIL the word “shibboleth” - I like it!
If anyone is interested in a declarative validation library for python you might want to check out pydantic. It’s not a direct analog to applicative parsing, but you can use validator functions and get back a list of validation errors. By default, it does throw a ValidationError though so if you want something that doesn’t throw errors then you’ll need to do some wrapping.
This is the default mode of validation into a database in Elixir, e.g: https://github.com/benawad/dogehouse/blob/f7d0cf89a241438f74b0c546dd93dffca95221f8/kousa/lib/beef/schemas/user.ex#L111