Most modern typed languages support encoding this information in the type of the variable, so that the compiler catches it, instead of in the variable name. Alexis King wrote a blog post about it that reached the front page a few days ago. https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-va...

Did you read the article?

The correct way of doing Hungarian Notation, as demonstrated in this article, is to encode in the name of the variable what it does, not its type.

No compiler can know that a string variable is a "name" or a "password".

It still might be possible to have types like `UserId` or `UserCredential`, which get parsed at the request time and never roll back to plain strings. It is evident that they are indeed different subtypes of strings: user identifiers can't have a space for example.

That said, I generally agree that conventional type systems are not (yet) capable of encoding all those informations to a type. I prefer a mix of actual types and coding conventions for the practical matter.

I'm curious what type of capabilities are lacking in the Ada or the F# type system to handle this. You can create 'new' (hear incompatible) types instead of subtypes and they won't be affectable to each other. So

   type User_Id_Type is new String;
   type User_Credential_Type is new String;
Then in your request deserializer convert once from String to the correct type, and at the use site only allow taking the correct type?

You can also add static predicates to ensure User_Id_Type doesn't contain spaces. You can also make the type 'opaque' and it won't be possible to convert to a String without a bespoke interface.

I'm not sure what's missing. I almost feel like it would be a great challenge :-)

I think in this particular case they suffice, because they are clearly different subtypes as I've mentioned.

It is much harder, if not impossible, if you have to describe a relation between two or more values of the same subtypes. For example, imagine that you already "know" certain indices never go off the boundary of given array but want to encode that information to types. Sure, there exists a Rust crate [1] that tracks a relation between indices and originating array via lifetime, but it is complicated.

[1] https://github.com/bluss/indexing