Hacker News new | ask | show | jobs
by meetups323 1941 days ago
In TS this would be flipped:

    type Person = {
     name: string;
     age: number;
    }

    type SomeFieldsMissing = Partial<Person>;
    type AllFieldsRequired = Required<Person>; // No real change in this case
Where Partial and Required are defined like:

    type Partial<T> = { [P in keyof T]?: T[P] | undefined; }

    type Required<T> = { [P in keyof T]-?: T[P]; }
What would you think of something like this ("mapped types")? They can get fairly powerful: https://www.typescriptlang.org/docs/handbook/2/mapped-types....
3 comments

that seems like something that would only really work with a type system that's as structural as TS (vs rust, which is very nominal)
What would happen if I wanted only some fields to be required? To borrow from the GP example, something like this:

    struct Person<F> {
        pub name: F<String>,
        pub age: Option<u32>,
    }
With `Person<Box>` still leaving `age` as optional.
You can do it like this:

  type RequiredKeys<O, K extends keyof O> = Omit<O, K> & Required<Pick<O, K>>
  type Person = {
    name?: string
    age?: number
  }
  type RequiredName = RequiredKeys<Person, 'name'>
The opposite (making some of the fields optional, without affecting the rest) would be written like this:

  type OptionalKeys<O, K extends keyof O> = Omit<O, K> & Partial<Pick<O, K>>
Yes, you can do this in typescript, but you can also lie about it in typescript. To do it provably, you would construct a function that accepted the more generic version and then, using type assertions (ie the typescript compiler infers the type from declared runtime behavior), it would return a narrowed type.

If one caller passes our type narrower with {age:F<u32>} then the return value of the function will be narrowed to that type, but a broader criteria can still be specified for the generic instance.

Add an extra generic parameter.

(Also, it shouldn't be Box; you'd want a bog-standard generic newtype for this, not an extra heap allocation.)

yeah, I picked Box mostly as a pedagogical instance of the identity functor that already exists in the stdlib - in the real world you'd definitely want `struct Identity<F>(F)` instead.
Yeah there's going to be tradeoffs either way, the question is how often they come up in the situations the language is designed to target (versus random examples designed to showcase some obscure corner of PLT)... In this case for TS you might craft your own mappings that work like AllButXOptional<Person, 'age'>, or similar. So it's controlled externally versus internally, which who knows if thats better or not. One benefit of the TS approach is that it's a bit more composable... you can imagine things like OptionalIfRequiredInOther<ComplexPerson, SimplePerson>.
That superficially looks horrible. How do you write `Person (Compose Maybe Maybe)`, even, never mind anything more sophisticated?

Edit, for that matter, how do you deal with a `Person f` that includes a `f (Maybe Something)` without confusing "the user didn't specify a `Maybe Something`" with "the user specified a `Maybe Something` with value `Nothing`" (or vice versa)?

I'm not sure what you mean by `Person (Compose Maybe Maybe)`? Is that a person with values you have to unwrap twice to use? That's not really a thing in TS, it either is undefined or isn't. But if you need to shoehorn Haskell into TS for whatever reason I guess you could do something like:

    type Maybe<T> = {hasValue: false} | {hasValue: true, value: T}
    type Shoehorn<T> = { [P in keyof T]-?: Maybe<Maybe<P>> }
> I'm not sure what you mean by `Person (Compose Maybe Maybe)`? Is that a person with values you have to unwrap twice to use?

`Compose Maybe Maybe T` (equivalently `Maybe (Maybe T))`), has N + 2 values (where N in the probably-infinite number of values T has):

. N values consisting of a T

. 2 different values that do not include a T

It's also equivalent (via 1 + (1 + N) = (1 + 1) + N) to `Either Bool T`.

And yes, `Person f` contains values of type `f Whatever`.

> it either is undefined or isn't.

Values are never undefined. They might be defined as a object that's called "undefined", but that is a specific, defined, value. And, apropos of the above, it's a single value, out of the two distinct non-T values being represented. Which is part of why the example at https://news.ycombinator.com/item?id=26277598 looks horrible - it seems like it would confuse the two different non-T values with each other if `Person f` includes a `f (Maybe T)`.

Edit: actually, clarified the previous comment a bit, too.

> > it either is undefined or isn't.

> Values are never undefined.

I am referring to the JS concept of the singleton `undefined`, not an abstract idea of definedness.

Overall I see where you're coming from, but scrambling Haskell and TypeScript syntax is severely impacting legibility.

End of the day Haskell and TS have very different use cases, one is a research language exploring the reaches of type theory, the other is type system bolted onto a dynamic language doing the best it can. Sure there are some concepts that it can't express, but that's entirely missing the point.

I don't know what kind of software you write, but I build the same CRUD app and UI-flavor-of-the-month frontend, over and over.

I have no clue what "N + 2 values (where N in the probably-infinite number of values T has): N values consisting of a T" means and I've done fairly well for myself.

Why does anyone need this to write and deploy CRUD apps and put forms + buttons on stuff?

Parent's Haskell background is leaking, but the idea is "wouldn't it be cool if you could have a value that was either "A box containing a box containing a Person", "A box containing a box containing nothing", or "A box containing nothing""?? How supremely useful and not at all confusing would that be?

I kid, but I admit there is a valid division between coding to explore the consequences logical frameworks and coding to extract money from corporations while providing them a way to keep their orders coming in and accounted for. I'll gladly spend time on both sides of the spectrum, depending on how many drugs are in my system and how much food is in my belly :)