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

Not sure what you're getting at? Even in Haskell you can easily pass data around as untyped maps of maps if you want. But usually you don't. But maybe you're talking more about when generics and obsession with not duplicating any code ever makes that part of the type system needlessly complex



i'm talking about the type system itself: keeping the type system implementation itself simple rather than just offering an unsafe work-around

this means foregoing system-enforceable type safety and features in some cases as the language level, in order to keep the type system fast and understandable to normies.


The type system can be understandable to normies while the implementation is not. I would rather have a type system that is correct and consistent, with the complexity hidden away, than to have to know where the boundary is in the 80/20 system, which seems like the perfect place for bugs to enter your code.


Agreed. Consistent is WAY better for me than inconsistent. One of the reasons I hate TypeScript so much is that it provides type safety except when it doesn't, and it takes a lot of experience to actually track down the MANY (non-obvious) places where it doesn't.


Could you provide some examples of where it doesn’t (assuming strict = true)?


I could provide a novel, but I'll try to keep it to just a few.

Class methods have incorrect variance, even with the 'strictFunctionTypes' setting: https://www.typescriptlang.org/tsconfig#strictFunctionTypes. What's even more insane is that interface/type methods will have different variance depending on the syntax used to write them:

    type SafeFoo = {
        // This will have correct type variance
        foo: (x: string | number) => void
    }
    
    type UnsafeFoo = {
        // This will have incorrect type variance
        foo(x: string | number): void
    }
The `readonly` feature doesn't fully work for object types:

    type Foo = {
        foo: number
    }
    
    type ReadonlyFoo = {
        readonly foo: number
    }
    
    function useFoo(f: ReadonlyFoo) {
        // f.foo = 4 // compile error
        const f1: Foo = f
        f1.foo = 4 // trololol, I just mutated the input even though I promised it was readonly
    }
Classes are kind-of also an interface, which leads to inconsistent/surprising behavior:

    class Foo {}
    
    function useFoo(f: Foo) {
        if (f instanceof Foo) {
            console.log('Cool, a Foo.')
        } else {
            console.log('Uh, wut?')
        }
    }
    
    useFoo(new Foo()) // prints: 'Cool, a Foo.'
    useFoo({}) // prints: 'Uh, wut?'
"TypeScript is structurally typed"... except when it's not. In my above example with the class pseudo-interface, you can "fix" it by including a private field in the class:

    class Foo {
        #nominal: undefined
    }
    
    function useFoo(f: Foo) {
        if (f instanceof Foo) {
            console.log('Cool, a Foo.')
        } else {
            console.log('Uh, wut?')
        }
    }
    
    useFoo(new Foo()) // prints: 'Cool, a Foo.'
    useFoo({}) // This is now a compile error!
Record types are incorrectly typed/handled. A Record<string, string> implies that for ANY string key, there will be a string value. What you almost always actually mean is: Record<string, string | undefined>. Yes, there is a compiler setting (https://www.typescriptlang.org/tsconfig#noUncheckedIndexedAc...) to make all array and record accesses treated as possibly undefined, but that should only be necessary for arrays. The Array type has only one type parameter: the codomain. It has no way of communicating what the domain is (what indexes have values). A Record, on the other hand, DOES allow us to specify both the domain and codomain, so when I write Record<string, string> I'm telling the type system that every string key will be mapped to a string value. Therefore, it should not allow me to assign something that clearly cannot fulfill that contract to it, but it does:

    const x: Record<string, string> = {}
In a type system sense, Record<K, V> is more-or-less equivalent to a function type: (K) => V. Imagine if TypeScript handled actual functions similarly:

    const x: (s: string) => string = (s) => {}

There's more, but like I said, I don't want to spend all day typing...


Wow, thanks for providing those examples! I’ve been using TypeScript for a while and haven’t ever taken care to notice when the type system is failing me.


Agreed. Leave out the inheritance, covariance, contravariance, and nullable types.

Leave the type system simple enough that you can take a rest and let the computer do the typing work for you.


hard agree on nullable types

just make everything non null and use option/maybe to indicate absence

wayyyy simpler


> Leave out ... covariance, contravariance

This means giving up on first-class functions.


Isn't this only true in a type system with subtyping? If it does apply to a type system without subtyping I would genuinely like to know if you have any references for it. I was under the impression that variance, as a concept, was immaterial for anything else.

Additionally, why would having only invariant functions mean leaving 'first-class' functions? Even assuming subtyping relations between types (simple or complex), invariant functions would still be 'first-class', just not acknowledging of the subtyping relation.


Parametric polymorphism gets you subtyping for function types even if you don't have it in general. `forall a . a -> a` is a subtype of `T -> T` for any particular `T`, for instance. And if you don't have subtyping or polymorphism, then talking about "leaving out" co/contravariance doesn't really make sense: there's nothing left to vary.


Nothing left to vary -> no variance -> no co/contravariance.

So... first-class functions with no covariance, contravariance?


Not that a big a deal, honestly.


Can you please explain why


Function types are contravariant in their inputs. It's the prototypical case, really.




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

Search: