Hacker News new | past | comments | ask | show | jobs | submit login

>A string value is not a great type to convey a user's email address or their country of origin. These values deserve much richer and dedicated types

this is a classic case of not needing more types but needing proper names. Types as concretions, i.e. simply collections of data or functions are a terrible idea because they're static and don't accrete. Data in the real world always does. This becomes very obvious when you go down a paragraph and you see the conundrum:

>For example, let's have a second type called VerifiedEmailAddress. If you wish it can even inherit from an EmailAddress. I don't care, but ensure that there is only one place in the code which can yield a new instance of VerifiedEmailAddress

okay, and for the next email setup let's have a third type, and a fourth type, and a fifth type, and so on. The end result of this is a zoo of types that help nobody to understand anything. It reminds me of an older Rich Hickey talk. When you program a delivery truck you don't make a type for each different truck because of the contents of the truck, you just take your delivery out of the truck and you don't care about the rest.




No, there should only be the one EmailAddress type. If it's not valid, it's not an EmailAddress.

Does having an EmailAddress type guarantee you won't accidentally accept crap? No, but when you get it wrong, you edit the validation in one place in the system.


If that place is the EmailAddress type, then you have built your system wrong. You check that stuff when the data enters the system.


> You check that stuff when the data enters the system.

There can be N entrypoints where data enters the system (different controllers, CLI), so you must always remember to validate emails in N places, otherwise broken data could end up being passed to business logic. Data can also be constructed inside the system. It's nice to have one centralized place where email is validated. Placing it in the constructor of a special type and using only that type for email guarantees it's impossible for business logic to receive invalid emails in principle, no matter what you do, because when an exception is thrown from a constructor no object is created at all. No object = no invalid data to deal with. You know that when you see EmailAddress (or any other type where state is validated in the constructor) it's in a valid state, there's no ambiguity, and, in my opinion, it's also more readable than just some string, the intent is clearer.


If you can construct an EmailAddress, then you have a valid EmailAddress. That's the point.

If an EmailAddress can be a valid or invalid email address, then just leave it as a String (since that can also be a valid or invalid email address).

> You check that stuff when the data enters the system.

Yes

> If that place is the EmailAddress type, then you have built your system wrong.

No

If you validate & construct an EmailAddress from another external class, that means external classes are free to bypass validation and construct an invalid EmailAddress. Putting the validation/construction inside EmailAddress lets you force construction to go via validation.


My advice: Relax and don't argue. People who don't understand that constructing an EmailAddress type is also validating the raw email string (in this case) will never understand it. They'll remain convinced for a very long time, possibly the rest of their lives, that they know better. That passing a string around is fine as long as either you always validate it everywhere (yes, kill your performance, that's smart) or that they validated it once and they pinky swear to never change the value and to always call validate before passing it into the system.

Let them find subtle errors in their programs over time, it's job security for them. They don't want to move on to new and more interesting things they just want to keep fixing the same shit for the rest of their careers.


Your comment reads as extremely harsh but I'm also tired of this argument at this point.

In real life, I don't actually work with anyone arguing against this stuff.


> this is a classic case of not needing more types but needing proper names.

Those are types.


no. a type is a description of a set of values and its associated operations. Types impose global meaning on entities in your program. When something belongs to a certain type receivers of arguments of that type lose control over how to interpret them. Thus types introduce coupling.

Names are just labels attached to an entity for the purpose of identification and readability, they don't impose meaning.


     newtype ArbitrarilyLabeled x = ArbitrarilyLabeled x
     
     forgetLabel :: ArbitrarilyLabeled x -> x
     forgetLabel (ArbitrarilyLabeled x) = x
What's the "global meaning" of `ArbitrarilyLabeled`? What control has `forgetLabel` lost?


> you just take your delivery out of the truck

Sorry, I accidentally took the delivery out of the email. You made them both have a deliver_to(address) method, you spent most of your comment talking about emails and the computer surely didn't stop my underslept human self from confusing an email address from a physical one.


then you should complain and check what's in your mail. The fact that a delivery method is generic isn't a problem, delivering things from A to B is a generic task. The recipient of the packet handles the content, the deliverer doesn't care what's in the box. deliver_to ought to be reusable, there shouldn't be 50 versions of it.

When we send json over the wire do we rewrite methods globally to make sure we're all in sync about the content? No, you as the message recipient make sure that what you got makes sense and how to deal with it.


> then you should complain and check what's in your mail

Now you've wasted time and resources.

> deliver_to ought to be reusable, there shouldn't be 50 versions of it.

Reality almost never pans out that way.

> No, you as the message recipient make sure that what you got makes sense and how to deal with it.

Indeed! May I introduce you to typed parser combinators?


Dunno, but adding verified attribute with a setter which executes verification step looks simpler to me and covers 80% of the use cases.




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: