4 ms·
Ah I think we're talking about subtly different things. You can definitely use generic goodness (`Generic` in Haskell and shapeless in Scala) to create generic
by dwohnitmok 6y ago
Ah I think we're talking about subtly different things. You can definitely use generic goodness (`Generic` in Haskell and shapeless in Scala) to create generic conversions between different types, but the result of each conversion is not itself a user-friendly type.
In the case of shapeless the general pattern is to have a starting type, use something like LabelledGeneric, do a ton of HList transformations, and then transform back. But while you're living in HList land those are in fact HLists and not, say, case classes.
Indeed, you must always write out the final type yourself (e.g. create a new case class). Even if you're using only HLists and completely eschewing case classes (which is both ergonomically painful but also has a pretty large performance hit), shapeless's functions that generate new HLists (as opposed to extracting an element) almost always require you to annotate their use sites IIRC, which is effectively the same thing as needing to write out the whole case class.
To put it another way, with shapeless in Scala 2 you could prove that a certain type was the intersection of two other types and create the generic transformation to do so. But this isn't true intersection types because you still need to write your starting types and your final type. That is in Scala 3 you `T0 & T1` is a type in and of itself; you don't need to rewrite all the fields in common between T0 and T1 and assign it to a new type (and then e.g. use shapeless to prove the relationship between your types).
In the same way what you've demonstrated here aren't mapped types, conditional types, etc., but rather a way of using shapeless to prove that a certain type is the mapped type/conditional type etc. of another type. The former is what I meant when I said "letting me generate any combination of fields wrapped in any combination of types from a single type definition."
Shapeless doesn't get rid of the problem that "Higher-Kinded Datatypes" (HKD) is trying to solve, since you still have to write all the duplicate types by hand for any reusable function.
Moreover, apart from the tedium of rewriting all these types, the bigger issue is that TS's type system lets you stay entirely at the value level, whereas shapeless requires things to become type level computations, effectively bifurcating your code into "shapeless transformations" and "ordinary functions acting on e.g. case classes."
For example, the equivalent of your library in TS would have each step just be a normal function like any other rather than a special type level computation. It can be applied to a normal object directly and can be tested and reused just like any other function.
That match types page on Scala 3 I think undersells the differences. The real power of a lot of TS's type level computations is how they interact with each other and typeof (see my other example in this thread of lightweight dependent types).
All that being said, I really like the concept behind your library and would love to see it updated for Scala 3!