Live data from Hacker News

Parse, Don't Validate – In a Language That Doesn't Want You To

cekrem.github.io

51–60 of 107 posts

Re: Parse, Don't Validate – In a Language That Doesn't Want You To

#51
post #21
post #19

Earlier quoted context omitted.

If nothing else, it should be done as a courtesy to those who would like to avoid such content. If the result is better for having used AI, why wouldn't an author want to disclose it?

Should they disclose the use of a spellchecker? A translation app? Gramarly? A writing tutor?

If there were groups that voiced a desire to be informed of that, then it would indeed be courteous to do so.

Re: Parse, Don't Validate – In a Language That Doesn't Want You To

#53

The author found out about the square holes in round peg situation with TS. Functions can implicitly error, and there's no annotation that's enforced to tell you that it might error. FP solves this with Result/Option, but this doesn't fit in TS. Effect is there to find a solution but will fail. Zod is the acceptable middleground in my opinion. Zod will allow you to throw a schema against an object and it'll tell you…

FYI branded types and newtypes are kind of the same thing, branded types just use a unique symbol that's expressed explicitly.

Re: Parse, Don't Validate – In a Language That Doesn't Want You To

#54
post #22

"TypeScript is structurally typed, which means two types with the same shape are the same type. string is string is string" I don't speak typescript so am probably missing something obvious. but. why would you parse an email(or anything really) into a string? (or string equivalent) When parsed it will end up as a specific email object, that is, something closer to a C struct. What is the articles dance doing?

Javascript doesn't have structs. The idea is that you have data on one hand and you have type witness about that data on the other hand. Type witness is something for the type system. But here you encounter the limits of structural typing versus nominal typing, because structural typing isn't able to witness that directly. In sufficiently strong nominal type systems, I can hide the constructor for an EmailAddress typ…

Javascript does have structs, it calls them objects.

If I parsed an emailAddress the thing that came out it would look like {'domain':'example.com', 'user':'john-doe'} or emailaddr.domain emailaddr.user and a emailaddr.address method if you like that form. Even if what I parsed ended up as a single string-like field, I would still name that field. emailaddr.address

Salutes for the bit on hiding the constructor, that makes a lot of sense.

It probably does not help anything that in my one attempt at making a javascript web application I did not bother trying to understand how javascript likes it's objects and just forced a python looking model onto it. If any of the web development team saw my code I would definitely get laughed out of the club.

Re: Parse, Don't Validate – In a Language That Doesn't Want You To

#55
I don't like zod. I want to define my types, not write schemas. And I don't like that then I have to use the types derived from those schemas rather than types I've defined myself directly.

So I just define my types and then use typescript-json-schema or similar to build a JSON Schema at build time (i.e. from an npm script) which then I use to validate input using ajv.

The only thing I do on top of that is to use annotations like "@minimum 0" (or, in the email example, "@format email") where the base types are not enough, but those simply go inside comments.

So the compiled package only has ajv as runtime dependency (which you're likely to have anyway, as it's everywhere), you're just defining regular types with some annotations on top and use a dev dependency to build you the JSON Schema. And as popular as zod is, I think JSON Schema is more of a standard and likely to stay with us longer.

I also reference those generated JSON Schemas from my OpenAPI definition, as a bonus.

Re: Parse, Don't Validate – In a Language That Doesn't Want You To

#56

Zod is by far the most ergonomic way to express those ideas in TypeScript these days. I miss it when writing code in other languages. The friction with the rest of the ecosystem is real, though. Most code out there expects you to handle errors with exceptions. I get the impression that polymorphic return types could get in the way of JSC/V8/SpiderMonkey's JIT, but I haven't measured it and I'm not sure of the actual…

> I miss it when writing code in other languages.

You can use Pydantic in Python and serde_derive in Rust. I assume most languages have a thing like that.

Re: Parse, Don't Validate – In a Language That Doesn't Want You To

#57

Earlier quoted context omitted.

It's pretty trivial to create derived and augmented types with Pick, Omit, Required, Partial. Combined with a few parsing functions that return an object typed to whatever specification you need and you are set IE: type User = { name: string; verified: boolean; email?: string; lastName: string; birthday?: string | { year: string; month: string; date: string; }} type Birthday = Required >; type UserWithBirthday = User…

creation is not a problem, maintenance is. Suppose you want to add one more property to VerifiedUserWithBirthday and UnverifiedUserWithBirthday, you might get 2 more new types, and somewhere at the higher layer call chains you need to know which enclosing type you should pass so that some method in the bottom chain will accept it. I am sure there are more elegant ways, but I am struggling to generalize it to most ent…

Yeah that's the engineering part in software engineer :)

If you have VerifiedUserWithBirthday, any value that fails the parsing function is implicitly UnverifiedUserOrUserWithoutBirthday... No need to define it separately. You get the inverse type for free IE a value that is of type User and not of type VerifiedUserWithBirthday.

A new property doesn't mean a new derived type. Only if that new property impacts what a VerifiedUserWithBirthday should represent should the VerifiedUserWithBirthday type be updated and even then, it's not a new type, just an update to an existing type. Again minimal updates needed.

The compiler handles all the validation and will tell you exactly where there are any issues - the compiler is what makes the maintenance cost quite low.

Re: Parse, Don't Validate – In a Language That Doesn't Want You To

#58

Earlier quoted context omitted.

The computer-science answer to this problem are called "refinement types", where you can attach arbitrary predicates to a type, e.g. (pseudo-code): fn send_birthday_mail(user: {u: User, u.birthday != null}) Contracts are a similar solution that restricts the predicates to only appearing in function types. The difference between this and an assert is that it gets checked at compile time (it can get quite expensive to…

this looks cool, but you are doing validation when accepting the object, you probably can't do it excessively, for example, if you are dealing with objects with heights, you might have a HumanLikeHeight where height range is between 40cm and 250cm, and you want to send email to that human, would you keep adding these conditions to the predicates?

Languages with refinement types (or contracts) like Dafny and Liquid Haskell can typically handle numerical predicates directly. Some can even handle string predicates directly, including regular expressions. They also allow you to write complex predicates as separate functions, albeit with limited expressiveness.

But you hit performance and/or outright computational limits (halting problem) rather quickly.

Re: Parse, Don't Validate – In a Language That Doesn't Want You To

#59

  default: {
    const _exhaustive: never = result;
    return _exhaustive;
  }
...is not how people should implement an exhaustiveness check ever! An exhaustiveness check exhausts your knowledge about the world, it should throw an exception at runtime. Just returning the non-matched case is a recipe for disaster. Do this instead:

  default:
    ((value: never) => { throw new Error(`Missing case for value: ${value}`); })(result);

Re: Parse, Don't Validate – In a Language That Doesn't Want You To

#60
post #54

Earlier quoted context omitted.

Javascript doesn't have structs. The idea is that you have data on one hand and you have type witness about that data on the other hand. Type witness is something for the type system. But here you encounter the limits of structural typing versus nominal typing, because structural typing isn't able to witness that directly. In sufficiently strong nominal type systems, I can hide the constructor for an EmailAddress typ…

Javascript does have structs, it calls them objects. If I parsed an emailAddress the thing that came out it would look like {'domain':'example.com', 'user':'john-doe'} or emailaddr.domain emailaddr.user and a emailaddr.address method if you like that form. Even if what I parsed ended up as a single string-like field, I would still name that field. emailaddr.address Salutes for the bit on hiding the constructor, that…

Yeah, in your example the structure is sufficiently dissimilar to a string for TypeScript not to confuse them for each other. However, if you also have an identity provider returning UserInfo objects in the form of {'domain':'example.com', 'user':'john-doe'}, you might not like it that now any email address is a valid UserInfo object. On the type level in TS, you cannot tell those types apart. But I guess you figured that out already.
Post reply on HN