8 ms·
> Go decided to not have generics, to keep the language easier to learn and more approachable. It's hard to argue with this one. Plain parametric polymorphism
by catnaroek 10y ago
> Go decided to not have generics, to keep the language easier to learn and more approachable. It's hard to argue with this one.
Plain parametric polymorphism is super easy to understand. Standard ML could be learnt in a week by someone who doesn't know how to program.
Admittedly, the interaction between parametric polymorphism and subtyping is tricky and subtle. And it seems most programmers have gotten used to taking subtyping for granted. But what if subtyping isn't always a good idea? Say, because it forces you to reason about variance (which humans always seem to do wrong!).
(inb4: Yes, Go has subtyping. When a struct conforms to a given interface, that's subtyping.)
- knucklesandwich 10y agoThe biggest irony of Go is that they have invariant parametric types for channels, arrays, etc. and indeed primops like channel sends, make, len, etc. are parametric functions. So for all the defensiveness about how parametric polymorphism is "difficult to understand", Go programmers seem to deal just fine with it on a day-to-day basis. What they lack is the ability to let programmers introduce their own parametric types and functions, presumably because we're too dumb to be anything but consumers of this functionality.
- Jtsummers 10y agoIf we consider Go in the context of a systems (their definition) engineering language within Google's enterprise, limiting choice is not about how dumb the programmers are, but about ensuring conformity. Rewriting code is expensive. As my office knows well. We maintain lots of old embedded systems and have to periodically rewrite or rehost it because the old hardware platforms aren't available or aren't performant enough for new features. These become multi-year, multi-million dollar projects, for relatively little gain. By ensuring that developers and architects conform to certain conventions, it means (in theory) that this code maintenance is much cheaper, and that rewrites can be avoided or minimized. This is a good thing and lets organizations be more flexible and productive, as their time and money is no longer wasted on the old things, but can be spent on the new things.
- catnaroek 10y ago> By ensuring that developers and architects conform to certain conventions Enforcing conventions is of course a good thing! The problem is how Go enforces conventions: (0) When Go enforces a convention mechanically, it's a triviality that can be adequately handled by external tools (e.g., naming, formatting, unused variables, etc.). (1) When a convention is actually useful (e.g., the correct way of using an interface), Go's type system is too dumb to understand it, let alone enforce it. > aren't performant enough for new features Second-class parametric polymorphism (“generics”) is purely a compile-time feature. It can be completely eliminated (that is, turned into the non-generic code you would've written otherwise) using a program transformation called “monomorphization”, before any target machine code is generated. So there's no runtime price to be paid.
- chrismonsanto 10y agoTo be precise, you need to outlaw polymorphic recursion to be able to do full monomorphisation. I'm not sure if that's what you meant by "second-class" in this context
- catnaroek 10y agoFirst-class polymorphism is what System F gives you: functions from types to values. Second-class polymorphism is what Damas-Milner gives you: let-bound identifiers may admit more than one type, in which case every type they admit is subsumed by a type schema. Second-class polymorphism rules out polymorphic recursion if you consider every recursive definition as syntactic sugar for applying a fixed point combinator to some expression of type `a -> a`, for whatever monotype `a`.
- chrismonsanto 10y agoThat's a new turn of the phrase for me. I've only ever heard the expression "second-class parametric polymorphism" used in reference to enforcing predicativity (which does not rule out polymorphic recursion)
- stcredzero 10y agoWhat they lack is the ability to let programmers introduce their own parametric types and functions, presumably because we're too dumb to be anything but consumers of this functionality. Here's what my experience is with certain features in languages: They enable some programmers to do great things, while also enabling a few programmers blinded by hubris to do maddening things. Over the life of a large project, the unfortunate coincidence of different pieces of hubris driven code sometimes causes an outsized amount of frustration. Analogy: Most people, most of the time, have the good sense to operate cars, drones, and high powered laser pointers without becoming a dangerous nuisance. However, there is a potential for a minority of users of such devices to cause far more than their share of public nuisance. Therefore, there are rules and restrictions about how such things are used by many people at scale. So yes, as an individual you are probably just fine. But you aggregated with a whole bunch of other programmers is likely to be a different story. https://en.wikipedia.org/wiki/Tragedy_of_the_commons https://en.wikipedia.org/wiki/Tragedy_of_the_commons
- knucklesandwich 10y agoYou can make this argument with nearly anything we naturally accept as a feature of a programming language. The ability to name things has a long history of abuse. The ability to define types, implement programming patterns, define new syntactic features via high order functions, use concurrency primitives. Hell, simply the idea of programming is rife with potential for abuse. This is a narrative without specifics, and unfortunately always where the conversation seems to end with gophers. We accept some amount of features that can be abused because they offer utility that outweighs their potential for misuse. So how exactly are generics a worse offender than these other features or a worse tradeoff for their utility? Because from my perspective, being able to define parametric data types and functions is a huge win for safety and terseness of code without a lot of downside.
- stcredzero 10y agoHell, simply the idea of programming is rife with potential for abuse. Exactly. Everything you add has a cost/benefit for a particular context. Evidently you disagree with how the Golang team has calculated cost/benefit with regards to generics. Because from my perspective, being able to define parametric data types and functions is a huge win for safety and terseness of code without a lot of downside. Terseness is a good thing? Some people say terseness is bad. Is safety the only issue or always the top priority? All production code exists in a specific context. It's best to tailor to your specific context. This may well mean that you may encounter a context where you do not want to use Go. http://anomaly.org/wade/blog/2013/07/coding_style_terse_vs_verbose_2.html http://anomaly.org/wade/blog/2013/07/coding_style_terse_vs_v...
- Gankro 10y agoHonestly parametric polymorphism is a big slippery slope feature. There's always Just One More Thing -- higher rank/order/kinded types, where clauses, dependent types, specialization... or else you force people to use dynamic checks/allocations/casts that reduce your type-safety and bog the code down relative to the "optimal" design. Don't get me wrong, I love me some parametric polymorphism, but it's by no means a simple thing as far as I've seen. Especially if you care about the effeciency of things (you can fudge a lot more wih lots of indirection/allocation, like Java does).
- catnaroek 10y ago> higher rank A language designer can provide let polymorphism, refuse to add more, and call it a day. > (higher) order/kinded types, This is orthogonal to parametric polymorphism. Higher-kinded types are problematic for inference, and the way Haskell has implemented them has unfortunate consequences for modularity. > where clauses, This is just syntactic sugar. (FWIW, what I think Rust needs is better inference, rather than ways to make type signatures less verbose.) > dependent types, This is unrelated to parametric polymorphism. > specialization This is antithetical to parametric polymorphism. > or else you force people to use dynamic checks/allocations/casts that reduce your type-safety and bog the code down relative to the "optimal" design. Standard ML doesn't have dynamic checks or unsafe casts, and I don't find myself longing for them.
- mafribe 10y agoThis is orthogonal to parametric polymorphism Higher-rank types are not orthogonal to parametric polymorphism, instead they are a special case. You can see this when you realise that rank-k polymorphism is a subsystem of System F (the paradigmatic typing system for parametric polymorphism) for any k. The let-polymorphism of the ML-family is just rank-1. See Chapters 22 and 23 of Pierce's great "Types and Programming Languages". Higher-kinded types are problematic for inference That is true, but already type inference for rank-3 polymorphism is undeciable, and the same is true for System F polymorphism. In practise, Haskell needs only few kind-annotations to make kind inference possible. This is helped by unannotated kind variables having kind * in Haskell (IIRC).
- mpartel 10y agoStructs implementing interfaces can be viewed as subtyping, but they don't have to be. An alternative is to take a typeclass-like view. Basically, a function "(A, A) -> A where A implements I" could be implemented as "(pointer to vtable of interface I, voidptr, voidptr) -> voidptr". Not having "implementation inheritance" between structs helps a lot, though I'm not sure if Go's anonymous fields might pose a problem.
- catnaroek 10y ago> An alternative is to take a typeclass-like view. Type classes alone don't give you anything like Go's interfaces. Type classes plus existentials give you something kinda like Go's interfaces, but requires explicit casts (in the form of unwrapping the contents of an existential constructor and putting them into another existential constructor). Type classes plus rank-N types can express Go's interfaces, but at that point Haskell already has subtyping, induced by subclasses. (Or else how do you think “a Lens is a Traversal” is possible?)
- reality_czech 10y agoI just spat out my coffee. Standard ML can be learned in a week by someone who doesn't know how to program? Have you ever actually tried to teach someone who doesn't know how to program? It takes months, even when using a simple language like Python. Or even BASIC, which was designed specifically for beginners. Standard ML is a good language (especially considering when it was developed, in the 1970s). Somehow a cult has grown up around it that prevents people from seeing that it isn't the solution to all problems, just another tool in the toolbox. Sad.
- catnaroek 10y ago> Have you ever actually tried to teach someone who doesn't know how to program? Yes. > It takes months, even when using a simple language like Python. Or even BASIC, which was designed specifically for beginners. I never said anyone can learn everything there is to programming in a week. I only said anyone can learn the Standard ML language in a week. You might encounter far more difficult things along the way, but they shouldn't be related to Standard ML itself.