← Back to context

Comment by jaen

12 hours ago

String literals are structural types which are way more expressive than regular (Haskell) ADTs, which are nominal types.

In TS in particular, in combination with other features (mapped types), they are equivalent to row polymorphism + whatever Haskell/GHC features enable type families to specialize on constant literal arguments (or you can use atomic types, but that's not structural / open-world)... so pretty advanced.

This is valid TS/Python:

    type ABC = "A" |"B" | "C"
    type AB = "A" | "B"
    const x: AB = "A";
    const y: ABC = x;

The equivalent Haskell requires using several extensions.

I know. I literally gave the example of a Python Literal in the post you're replying to. TS too. :)

My overall point is that Haskell's type system is sufficiently expressive (you may not have "A" | "B" | "C", but you do have A | B | C) that there's no obvious remaining use case for string literals, unless you're thinking of typing input by way of expected literals instead of actually parsing it, which is... a choice. :P

  • By Haskell's type system do you mean with all the GHC extensions?

    Because TypeScript has structural sub-typing, while standard Haskell (eg. `A | B | C`) has neither subtyping nor structural typing, which both are very useful features for safe "integration/glue" type of programs.

    (String) literals form a fundamental part of the TS "row polymorphism" (record types) and eg. tuple union type implementation.

    You can type a non-empty array that starts with zero...

        type Arr = [0, ...number[]];
        const a: Arr = [0, 1, 2, 3, 4]
    

    Now try in Haskell.

    • > By Haskell's type system do you mean with all the GHC extensions?

      No? What extensions does `A | B | C` require?

      > Haskell has neither subtyping nor structural typing

      Is subtyping back in? Good news for Java and C++.

      Re structural typing, I would ask what behaviour you're after, specifically. For example, this is a valid, typed Haskell function for any two values that can be added, including any user-defined ones:

          adder a b = a + b
      

      If by structural typing you mean silently coercing types that the compiler deems structurally equivalent, then no, but I don't think many people writing Haskell would consider that desirable. A `Person` may have an age (40) and a `Wine` may have an age (2005), but you're not going to get sensible results if you start adding those two together, and your compiler should probably stop you.

      Structural typing is the sort of thing that is very valuable if you're bolting a type system onto a language with a cornucopia of untyped structs, like JS objects. It is comparatively much less valuable if you're working in a typed ecosystem to begin with, since you're not liable to have loose untyped structs floating around that require coercion.

      > "integration/glue" type of programs

      It does sound a lot like you're using string literals in lieu of parsing foreign input, which strikes me as a pretty bad idea. Particularly in a language like TS, which is not type safe at runtime, and which will happily ingest an unexpected value, silently coerce it in all sorts of fun and wacky ways, and cause behaviour far removed from what any static analysis of the TS source would suggest.

      > You can type a non-empty array that starts with zero

      Can you please name me any possible actual use for this? Especially given the type doesn't even exist at runtime and will never be enforced on input data, so this is a once-off check for comptime constants?

      5 replies →