3 ms·
> Highly recommend people mess around with Purescript, you can feel how much pressure is relieved thanks to the row polymorphism tooling almost instantly. > I
by runeks 2y ago
> Highly recommend people mess around with Purescript, you can feel how much pressure is relieved thanks to the row polymorphism tooling almost instantly.
> I think row polymorphism is a fairly straightforward thing compared to dependent types in general, but can let you crush a whole class of errors [...]
Would you care to provide a few examples? I don't have experience with row polymorphism so I'm genuinely curious.
- tmountain 2y agogreet :: forall r. { name :: String | r } -> String greet person = "Hello, " <> person.name <> "!" greetWithAge :: forall r. { name :: String, age :: Int | r } -> String greetWithAge person = "Hello, " <> person.name <> "! You are " <> show person.age <> " years old." main :: Effect Unit main = do let person = { name: "Alice", age: 30, occupation: "Engineer" } -- greet can accept the person record even though it has more fields log (greet person) -- Output: "Hello, Alice!" -- greetWithAge can also accept the person record log (greetWithAge person)
- blue_pants 2y agoHow does it differ from structural typing in TypeScript though?
- tmountain 2y agoStructural typing relies on interface compatibility. Row polymorphism is a type-level feature in PureScript where record types are constructed with an explicit "row" of fields. In Practice, row polymorphism is more granular, allowing you to explicitly allow certain fields while tracking all other fields via a ("rest") type variable. Example: PureScript allows you to remove specific fields from a record type. This feature, is called record subtraction, and it allows more flexibility when transforming or narrowing down records. You can also apply exact field constraints; meaning, you can constrain records to have exactly the fields you specify. Lastly, PureScript allows you to abstract over rows using higher-kinded types. You can create polymorphic functions that accept any record with a flexible set of fields and can transform or manipulate those fields in various ways. This level of abstraction is not possible in TypeScript. These are just a few examples. In the most general sense, you can think of row polymorphism as a really robust tool that gives you a ton of flexibility regarding strictness and validation.
- blue_pants 2y ago> PureScript allows you to remove specific fields from a record type. This feature, is called record subtraction, and it allows more flexibility when transforming or narrowing down records. TypeScript does allow you to remove specific fields, if I understand you right [0]: function removeField<T, K extends keyof T>(obj: T, field: K): Omit<T, K> { const { [field]: _, ...rest } = obj; return rest; } type Person = { name: string; age: number }; declare const p: Person; const result = removeField(p, 'age'); // result is of type: Omit<Person, "age"> > PureScript allows you to abstract over rows using higher-kinded types. You can create polymorphic functions that accept any record with a flexible set of fields and can transform or manipulate those fields in various ways. This level of abstraction is not possible in TypeScript. Again, if I understand you correctly, then TypeScript is able to do fancy manipulations of arbitrary records [1]: type StringToNumber<T> = { [K in keyof T]: T[K] extends string ? number : T[K] } function stringToLength<T extends Record<string, unknown>>(obj: T): StringToNumber<T> { const result: Record<string, unknown> = {}; for (const key in obj) { result[key] = typeof obj[key] === 'string' ? obj[key].length : obj[key]; } return result as StringToNumber<T>; } const data = { name: "Alice", age: 30, city: "New York" }; const lengths = stringToLength(data); lengths.name // number lengths.age // number lengths.city // number [0] https://www.typescriptlang.org/play/?#code/GYVwdgxgLglg9mABAJwKYFs4DdUDEaoA2AJgDwAqANIgNKKoAeUqYxAzogNaoCecwicgD4AFHABGAKwBcg6sAIlZNAJSyA8uhhQK1GkMQBvALAAoRBcQQEbKEcQBtBUWIBdWQH1qAOl9pbiAC+iAC8iBKSANxmliioUCDISP5Q0aaBZmZQPAAOqIgACqjIbAih9mAAhuiosrbIMGAA5pGIlU21iGAg6OLFQWlmxKgQhJVoVjZ2ObJFJQiDptZgAf4ghHZhaJg4+C4iOdQA5O2oRyqtAPSXiJrapHOlYNQARKcvQmbXsYgAegD8QA https://www.typescriptlang.org/play/?#code/GYVwdgxgLglg9mABA... [1] https://www.typescriptlang.org/play/?#code/C4TwDgpgBAysBOBLAdgcwCoHsByBXAtgEYTwA86AfFALxQDeAsAFBStQDaA0lClANYQQmAGZR0AXQBcYruKgQAHsAjIAJgGco6hClRQA-FGQFi8KNPSzmAX2bNhuZAGNgiTMi060WADIrUwAAW5PJKKhpQAEoQTpjwqqTaSGgANFCOfMiYAO7IFBQAFJiEAFYWAJTScMkYOCYk5FSMLGyxyNpQ8BDquAA2wNLRsfGJXqhpGVm5VLR01gDczGxQwnFQBW0dAiA8HsUl5fRLy2xdPf3s23K0oJAiUPuXgtfUtADkSbpvBg+lTyDiAB0vX8QXMvxK-3EixarFssM6EGAuHgHjOfWAUAAhppqrosHgiA1KDD4cxNpjVFjgFiaEcEcgsfgINIAEQAQV6iCcEFZKWOrCxqBZUAAzAAGfkIpyIUBs7AQbJQACacT4rJsMPJ7g6ILQQU0tE+3kwfn1gQKVJp5S1TD1AUC6kBjOZUAA9G6jPV4Mx7QbAULoB6vUSfXbQY7ATLQO7PcZQ0A https://www.typescriptlang.org/play/?#code/C4TwDgpgBAysBOBLA... edit: provided links to TS playground
- tmoertel 2y agoCan `greetWithAge` be implemented using `greet`?
- runeks 2y agoThank you, but I was looking for real world examples solved by row types as OP implied there were plenty of.
- rtpg 2y agohttps://rtpg.co/2016/07/20/supercharged-types.html https://rtpg.co/2016/07/20/supercharged-types.html I wrote this example a long time ago, I think nowadays I could come up with some more interesting examples though. As to the differences with TS... I think they're playing in similar spaces but the monadic do syntax with Purescript lets you use row polymorphism for effect tracking without having to play weird API tricks. In TS that's going to be more difficult. (short version: in purescript I could write an API client that tracks its state in the types so that you can make sure you authorize before calling some mechanism. In TS you would need to design that API around that concept and do things like client = client.authorize(). Purescript you could just do "authorize" in a monadic context and have the "context" update accordingly)