4 ms·
> You'll need sendToUnverifiedEmail(email: UnverifiedEmail) and sendToVerifiedEmail(email: VerifiedEmail), and have code to get the right type to pass to the ri
by hither_shores 4y ago
> You'll need sendToUnverifiedEmail(email: UnverifiedEmail) and sendToVerifiedEmail(email: VerifiedEmail), and have code to get the right type to pass to the right function the in the right circumstance...
Only if you're using a language with an insufficiently strong type system (e.g. Java, C#)
in typescript:
type UnverifiedEmail = { address: string, verified; false }
type VerifiedEmail = { address: string, verified; true }
...
type Email = UnverifiedEmail | VerifiedEmail | FooBarBazEmail
const sendToEmail = (email: Email): Promise<void> = ...
in Haskell:
class SendTo t where
sendTo :: t -> IO ()
newtype Email = Email string
instance SendTo Email where
sendTo (Email address) = ...
newtype VerifiedEmail = VerifiedEmail Email deriving (SendTo)
newtype FooBarBazEmail = FooBarBazEmail Email deriving (SendTo)
> I.e., due to a bug, a value of type VerifiedEmail could be created for an email address that is not really verified. Then, your static checks for VerifiedEmail don't help you at all.
Of course they do - they tell you that the bug is in the verification code, and not in any of the thousands of lines of business logic separating it from the place where the error was found.
- jmull 4y agoI don't know Haskell, but in the typescript code the types aren't doing anything... sendToEmail sends to any kind of Email. And if the code wants to know if an Email is verified, it inspects the verified field. > Of course they do - they tell you that the bug is in the verification code, and not in any of the thousands of lines of business logic separating it from the place where the error was found. Whether you centralize the verification code (or otherwise have a separation of concerns for it) or not is independent of whether you use the type system to express when an email is verified or some other mechanism.
- hither_shores 4y ago> sendToEmail sends to any kind of Email. That's my point, you don't need a combinatorial explosion of behaviors for every possible most-specific-type, you can just reuse existing ones. > And if the code wants to know if an Email is verified, it inspects the verified field. That's exactly what you shouldn't do. Runtime "type" inspection is just bad overly distributed parsing. The point of typing is to move errors to compile-time. emailLaunchCodes = (email: VerifiedEmail): Promise<void> => ... This doesn't need to inspect anything, because the type signature guarantees that someone else has already handled that. > Whether you centralize the verification code (or otherwise have a separation of concerns for it) or not is independent of whether you use the type system to express when an email is verified or some other mechanism. Separating the verification code - which is table stakes, really - doesn't guarantee that you're not accidentally adding or losing "verification" elsewhere. Types can and should.
- jmull 4y ago> The point of typing is to move errors to compile-time. You've got to understand: Whether or not a particular email address is verified or not isn't something that's generally known at compile time... That means compile time checks cannot actually guarantee if that email address is verified or not. The compiler cannot know something that isn't known. There must be runtime code somewhere doing the check. What people are really talking about with types here is that if you organize your code in a certain way, you can put the code that verifies whether an email address is verified or not in one place and use types to help make sure other code doesn't accidentally ignore or change that guarantee. That's good and fine. But... (1) you can do the very same thing without types; (2) Either mechanism is only as good as your code organization and controls that ensure there's a single place this is determined (and that that place is correct).
- hither_shores 4y ago> Whether or not a particular email address is verified or not isn't something that's generally known at compile time Yes, obviously. What is knowable at compile time is the stuff that comes after: given that the input to this function has property X, does the output have property Y? Arguments, not premises. > But... (1) you can do the very same thing without types Sure, and if someone makes an SMT-solver-oriented language (that isn't just using it to drive type inference) I'll be happy to try it out. But what most people who dislike types claim is that you can replace them with testing (true in principle, false in practice: no one actually remembers to test every last edge case every single time) or just programmer discipline (lol).
- jmull 4y agoI like types just fine. They are great for expressing static constraints.