| ▲ | bri3d 7 hours ago |
| > Haskell gives you tools to encode these incantations in types so they cannot be forgotten. This is, for my money, the single most valuable thing the language offers a production engineering organization. Haskell is admittedly, probably the most powerful widely (or even somewhat widely) used language for doing this, but this general pattern works really well in Rust and TypeScript too and is one of my very favorite tools for writing better code. I also really like doing things like User -> LoggedInUser -> AccessControlledLoggedInUser to prevent the kind of really obvious AuthZ bugs people make in web applications time and time again. I've found this pattern to be massively underutilized in industry. |
|
| ▲ | dirkt 2 hours ago | parent | next [-] |
| > works really well in Rust and TypeScript too And of course Rust and TypeScript were heavily influenced by Haskell... they just don't mention it and call things differently, to avoid the "monads are scary, I need to write a tutorial" effect. Though it's less about monads and more about things like type classes. Imitation is the sincerest form of flattery. |
| |
|
| ▲ | miki123211 5 hours ago | parent | prev [-] |
| This isn't specific to Rust or Typescript. You can do this in basically any language. Imagine you have to distinguish between unescaped and escaped strings for security purposes. Even with a dynamically typed language, you can keep escaped strings as an Escaped class, with escape(str)->Escaped and dangerouslyAssumeEscaped(str)->Escaped functions (or static methods). There's a performance cost to this, so that's a tradeoff you have to weigh, but it is possible. Another way of doing this is Application Hungarian[1], though that relies on the programmer more than it does on the compiler. [1] https://www.joelonsoftware.com/2005/05/11/making-wrong-code-... |
| |
| ▲ | dasyatidprime 4 hours ago | parent | next [-] | | > There's a performance cost to this That part is (de facto) required for dynamically typed languages, but not for statically typed ones where the newtype constructor/deconstructor can be elided at compile time. Rust and C++ especially both do the latter by having true value types available for wrappers that evaporate into zero extra machine code. But then just this moment I wondered: do any major runtimes using models with no static type info manage to do full newtype elision in the JIT and only box on the deopt path? What about for models with some static type info but no value types, like Java? (Java's model would imply trickiness around mutability, but it might be possible to detect the easy cases still.) I don't remember any, but it could've shown up when I wasn't looking. | | |
| ▲ | gf000 an hour ago | parent [-] | | Well, java can do escape analysis, so a wrapper with a single field may end up as a local variable of the embedded field. As for other JVM languages like Kotlin and Scala, they have basically what "newtype" is, but it can only be completely erased in the byte code when they have a single field. |
| |
| ▲ | wyager 4 hours ago | parent | prev [-] | | > You can do this in basically any language. You can do it in Assembly. That doesn't mean it's cost effective. | | |
| ▲ | bonesss 2 hours ago | parent | next [-] | | And categorically: the issue isn’t what “I’d” do, my habits often match my habits, it’s what other project members will be doing (including future degenerate versions of myself assumed to be some combination of busy, tired, stressed and drunk). The Confucian philosophy that people act like water coming down a mountain, seeking the path of least resistance comes to play. Haskell, OCaml, F#, and their ilk can yield beautiful natural domain languages where using the types wrong is cost prohibitive. In languages without those guarantees every developer needs discipline to avoid shortcuts, and review needs increase, and time-pressure discussions rehashed. | |
| ▲ | myst 2 hours ago | parent | prev [-] | | Costs are a skill issue ;-) |
|
|