Live data from Hacker News

Names are not type safety

lexi-lambda.github.io

1–10 of 130 posts

Re: Names are not type safety

#2
> Newtypes are useful when carefully applied, but their safety is not intrinsic, no more than the safety of a traffic cone is somehow contained within the plastic it’s made of. What matters is being placed in the right context—without that, newtypes are just a labeling scheme, a way of giving something a name.

What's a better alternative though?

I like this approach for tagging e.g. user input that has been checked to be free of HTML/JS injection hacks to stop you from outputting user input that hasn't been checked first (Angular does something like this with TypeScript I think). Obviously you could tag something as safe that isn't by mistake but at least the places in your code this decision is made is explicit and you can't forget to do it.

I'm not seeing a practical alternative that would be significantly better. If you want to be able to capture any property you can think of in the type system (like bounded integers or bounded lists), you're inevitably going to need dependent types and they're about as far from practical as you can get (where either you or the computer will then have to write difficult maths proofs before you know your code is correct).

Re: Names are not type safety

#4

> Newtypes are useful when carefully applied, but their safety is not intrinsic, no more than the safety of a traffic cone is somehow contained within the plastic it’s made of. What matters is being placed in the right context—without that, newtypes are just a labeling scheme, a way of giving something a name. What's a better alternative though? I like this approach for tagging e.g. user input that has been checked t…

Applying the author's "parse, don't validate" concept, I think you'd parse the user input into a "safe HTML" type with a definition that excluded the language features that enabled those attacks. It might not be worth writing such code for a single project, but I could get behind a library that did that.

Re: Names are not type safety

#5

> Newtypes are useful when carefully applied, but their safety is not intrinsic, no more than the safety of a traffic cone is somehow contained within the plastic it’s made of. What matters is being placed in the right context—without that, newtypes are just a labeling scheme, a way of giving something a name. What's a better alternative though? I like this approach for tagging e.g. user input that has been checked t…

Applying the author's "parse, don't validate" concept, I think you'd parse the user input into a "safe HTML" type with a definition that excluded the language features that enabled those attacks. It might not be worth writing such code for a single project, but I could get behind a library that did that.

This is an approach I've used in C#/Java - you have 'UnvalidatedFoo' and 'ValidatedFoo' types, UnvalidatedFoo has a public constructor, ValidatedFoo has a private constructor.

The only way to acquire a ValidatedFoo is to pass an UnvalidatedFoo into ValidateFoo(). The outer layer of the code deals in UnvalidatedFoos, but past that point you must have a ValidatedFoo to continue.

Re: Names are not type safety

#6

> Newtypes are useful when carefully applied, but their safety is not intrinsic, no more than the safety of a traffic cone is somehow contained within the plastic it’s made of. What matters is being placed in the right context—without that, newtypes are just a labeling scheme, a way of giving something a name. What's a better alternative though? I like this approach for tagging e.g. user input that has been checked t…

The way to ensure type safety of new types is smart constructors: https://wiki.haskell.org/Smart_constructors

You have a constructor function which does the input check, and it lives in a module that only exports the smart constructor, not the type constructor. It doesn’t matter if another module deconstructs the type, since the input was already checked.

For example:

newtype Username = Username Text

mkUsername :: Text -> Either Text Username

mkUsername t = if t == “” then Left “Username cannot be blank” else Right (Username t)

If you don’t export the constructor for Username, and only export mkUsername, it doesn’t matter if someone deconstructs the Text from Username, because it must already be validated.

Re: Names are not type safety

#7

> Newtypes are useful when carefully applied, but their safety is not intrinsic, no more than the safety of a traffic cone is somehow contained within the plastic it’s made of. What matters is being placed in the right context—without that, newtypes are just a labeling scheme, a way of giving something a name. What's a better alternative though? I like this approach for tagging e.g. user input that has been checked t…

The way to ensure type safety of new types is smart constructors: https://wiki.haskell.org/Smart_constructors You have a constructor function which does the input check, and it lives in a module that only exports the smart constructor, not the type constructor. It doesn’t matter if another module deconstructs the type, since the input was already checked. For example: newtype Username = Username Text mkUsername :: Te…

The article gives some examples why smart constructors don't fully solve the problem in the section "Newtypes as tokens"

Re: Names are not type safety

#8
post #7

Earlier quoted context omitted.

The way to ensure type safety of new types is smart constructors: https://wiki.haskell.org/Smart_constructors You have a constructor function which does the input check, and it lives in a module that only exports the smart constructor, not the type constructor. It doesn’t matter if another module deconstructs the type, since the input was already checked. For example: newtype Username = Username Text mkUsername :: Te…

The article gives some examples why smart constructors don't fully solve the problem in the section "Newtypes as tokens"

Yeah, I don’t find those points very convincing. Obviously if you export other ways to construct the type, it doesn’t work...

It’s like saying “if you forget to sanitize input it won’t get sanitized.” Okay?

Re: Names are not type safety

#9

> Newtypes are useful when carefully applied, but their safety is not intrinsic, no more than the safety of a traffic cone is somehow contained within the plastic it’s made of. What matters is being placed in the right context—without that, newtypes are just a labeling scheme, a way of giving something a name. What's a better alternative though? I like this approach for tagging e.g. user input that has been checked t…

Maybe you can solve it like this: internally represent the document as DOM objects. Have element nodes contain a list of children which can be either raw strings (representing text nodes), or other element node objects. The render function of element nodes would know to escape the contents of text nodes before concatenating them with their rendered element node siblings. It would be type safe since it knows all the types of children it can contain.

It's like SQL parameterized queries: delay the concatenation until the last possible moment to reduce the area where errors can occur.

But I think maybe that just moves the opportunity for error to the render function, so I don't know if that is really an improvement on "smart constructors".

Re: Names are not type safety

#10

> Newtypes are useful when carefully applied, but their safety is not intrinsic, no more than the safety of a traffic cone is somehow contained within the plastic it’s made of. What matters is being placed in the right context—without that, newtypes are just a labeling scheme, a way of giving something a name. What's a better alternative though? I like this approach for tagging e.g. user input that has been checked t…

In Haskell, you would use an algebraic data type that can only hold legal values. You would typically only use a newtype when you're just giving a new name to the same type.

Having a Magnitude newtype over Int doesn't change the range of permissible values but does prevent using a Length where a Magnitude was expected, for example.

In all typed languages you'll eventually see a function with multiple arguments of the same type and have to check the documentation (or worse, implementation) to know what each is for. Having a zero cost type alias to make it clear is an improvement and IMO is a form of type safety.

Having a data type that can hold illegal values is a bad idea. Using an Int for Length implies you know what to do with negative lengths, for example. A newtype won't help there.

Post reply on HN