Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

This can be done in typescript. It’s not super well known because of typescripts association with frontend and JavaScript. But typescript is a language with one of the most powerful type systems ever.

Among the popular languages like golang, rust or python typescript has the most powerful type system.

How about a type with a number constrained between 0 and 10? You can already do this in typescript.

    type onetonine = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

You can even programmatically define functions at the type level. So you can create a function that outputs a type between 0 to N.

    type Range<N extends number, A extends number[] = []> =
  A['length'] extends N ? A[number] : Range<N, [...A, A['length']]>;
The issue here is that it’s a bit awkward you want these types to compose right? If I add two constrained numbers say one with max value of 3 and another with max value of two the result should be max value of 5. Typescript doesn’t support this by default with default addition. But you can create a function that does this.

   // Build a tuple of length L
   type BuildTuple<L extends number, T extends unknown[] = []> =
  T['length'] extends L ? T : BuildTuple<L, [...T, unknown]>;

   // Add two numbers by concatenating their tuples
   type Add<A extends number, B extends number> =
  [...BuildTuple<A>, ...BuildTuple<B>]['length'];

   // Create a union: 0 | 1 | 2 | ... | N-1
   type Range<N extends number, A extends number[] = []> =
  A['length'] extends N ? A[number] : Range<N, [...A, A['length']]>;

   function addRanges<
     A extends number,
     B extends number
   >(
     a: Range<A>,
     b: Range<B>
   ): Range<Add<A, B>> {
     return (a + b) as Range<Add<A, B>>;
   }
The issue is to create these functions you have to use tuples to do addition at the type level and you need to use recursion as well. Typescript recursion stops at 100 so there’s limits.

Additionally it’s not intrinsic to the type system. Like you need peanno numbers built into the number system and built in by default into the entire language for this to work perfectly. That means the code in the function is not type checked but if you assume that code is correct then this function type checks when composed with other primitives of your program.





Complexity is bad in software. I think this kind of thing does more harm than good.

I get an error that I can't assign something that seems to me assignable, and to figure out why I need to study functions at type level using tuples and recursion. The cure is worse than the disease.


It can work. It depends on context. Like let's say these types are from a well renowned library or one that's been used by the codebase for a long time.

If you trust the type, then it's fine. The code is safer. In the world of of the code itself things are easier.

Of course like what you're complaining about, this opens up the possibility of more bugs in the world of types, and debugging that can be a pain. Trade offs.

In practice people usually don't go crazy with type level functions. They can do small stuff, but usually nothing super crazy. So type script by design sort of fits the complexity dynamic you're looking for. Yes you can do type level functions that are super complex, but the language is not designed around it and it doesn't promote that style either. But you CAN go a little deeper with types then say a language with less power in the type system like say Rust.


Typescript's type system is turing complete, so you can do basically anything with it if this sort of thing is fun to you. Which is pretty much my problem with it: this sort of thing can be fun, feels intellectually stimulating. But the added power doesn't make coding easier or make the code more sound. I've heard this sort of thing called the "type puzzle trap" and I agree with that.

I'll take a modern hindley milner variant any day. Sophisticated enough to model nearly any type information you'll have need of, without blurring the lines or admitting the temptation of encoding complex logic in it.


>Which is pretty much my problem with it: this sort of thing can be fun, feels intellectually stimulating. But the added power doesn't make coding easier or make the code more sound.

In practice nobody goes too crazy with it. You have a problem with a feature almost nobody uses. It's there and Range<N> is like the upper bound of complexity I've seen in production but that is literally extremely rare as well.

There is no "temptation" of coding complex logic in it at all as the language doesn't promote these features at all. It's just available if needed. It's not well known but typescript types can be easily used to be 1 to 1 with any hindley milner variant. It's the reputational baggage of JS and frontend that keeps this fact from being well known.

In short: Typescript is more powerful then hindley milner, a subset of it has one to one parity with it, the parts that are more powerful then hindley milner aren't popular and used that widely nor does the flow of the language itself promote there usage. The feature is just there if you need it.

If you want a language where you do this stuff in practice take a look at Idris. That language has these features built into the language AND it's an ML style language like haskell.


I have definitely worked in TS code bases with overly gnarly types, seen more experienced devs spend an entire workday "refactoring" a set of interrelated types and producing an even gnarlier one that more closely modeled some real world system but was in no way easier to reason about or work with in code. The advantage of HM is the inference means there is no incentive to do this, it feels foolish from the beginning.

Typescript’s type system can run Doom.

https://youtu.be/0mCsluv5FXA


(derogatory)

I hope in 10 years we look to this typescript phase the way we see the coffeescript hype.

Coffeescript went obsolete because many of the features were directly folded into JS ES6.

If you think typescript will go the way of coffeescript. Well. First off, coffeescript was waaay less popular than typescript. Second, if it does it just means types will get folded directly into js.




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

Search: