Hacker News new | past | comments | ask | show | jobs | submit login

> For example, I've been examining generics recently, but I don't have in my mind a clear picture of the detailed, concrete problems that Go users need generics to solve.

This is sampling bias at work. The people who need generics have long since given up on Go and no longer even bother participating in Go-related discussions, because they've believe it will never happen. Meanwhile, if you're still using Go, you must have use cases where the lack of generics is not a problem and the existing language features are good enough. Sampling Go users to try and find compelling use cases for adding generics is not going to yield any useful data almost by definition.




> no longer even bother participating in Go-related discussions, because they've believe it will never happen

/raises hand

I like when tools are good, but I've basically written off Go as a tool for generating unsustainable code right now (and a big part of it is the odious options, either type-unsafety or code generation, for things that are trivially handled by parametricity). If things change, I'll be happy to revisit it, but the described thought process just ignores my existence. And that's fine from my perspective, because I don't need Go, but I'd still like it to be better in case I do need to use it.


I think the Go team would still like to understand your production uses that caused you to write off Go. What is your problem domain? How would you accomplish that in Go? How did you actually solve your problem with a different language?

For me, user provided data structures are the only thing that comes to mind (sync.Map for example) in my production use of Go. But even then, my pain (time spent, errors made) due to a lack of generics is low.

You may have written off Go. But if you're willing, I bet the Go team would still like to read your production code use-cases that caused you to do so.


My "problem domain" is "good, correct code". I write code in many different spaces, from web software to mobile apps to (a lot of) devops tools to (less these days) games. My criticism of Go isn't "it's not good at code for my problem domain," it's "it's not good at code for your problem domain, either, and your workarounds shouldn't need to exist."

User-provided data structures are a big one. (A language where I have to copypaste to have typesafe trees is a bad language.) But, beyond that, I build stuff to be compiler-verified. Here's a trivial example that I did yesterday that I straight-up can't do in Go, but did in C# as part of a object graph rehydration routine (where object A required pointers to object B from a different data set, and since it's 2017 we aren't using singletons so the JSON deserializer takes in converters that look up objects based on keys in object A's JSON representation):

   public interface IKeyed<T> { T Key { get; } }
Just being able to do that on an object is powerful. C# has reified types; I know what T is at runtime. (I can't specialize on the type in C#, but I can fake it.) But I also know what it is at compile-time, and so I can't accidentally pass `IKeyed<String>` and `IDictionary<Int32, DependentObject>` to the same function because type constraints, yo.

I don't really care about fast code, because computers are all future-computers-from-beyond-the-moon. If I'm using a statically-typed language, I care about correct. Duplicated code is code that will--not may--lead to bugs. State I have to maintain in my head (like "this is an int map, talking to an int-keyed object set") is state that will--not may--lead to bugs.

If you're a statically-typed language that isn't helping me avoid bugs, you might as well not exist. You don't have to be whomping around stuff like Scala's Shapeless--I would argue that you shouldn't--but you have to provide at least a basic level of sanity and the difficulty of expressing stuff outside of parameterized types (even when there are workarounds) makes it not worth the hassle. I'll cape up for damned near everything in at least some context, from dynamic languages like Ruby and ES6 to Java (well, Kotlin) or C# to Modern C++ to Scheme or a Lisp. My no-buenos on programming languages are limited pretty exclusively to C and Go because both languages encourage me, encourage all of us who use them, to write bad code.


I'm new to Go and actually and don't do professional IT work but I immediately felt the need for having a type that allowed me to store any type.

My use case was/(still is) writing a spreadsheet where a user can enter different stuff in a cell and I want to store the value in an underlying data type. I now ended up storing everything as string because I couldn't figure out an efficient way to implement it.

My goal would have been to have one generic type that can store the cell types text and number which I can store in an array. This generic type could then implement an interface method like format() which calls a different format method depending on the type the cell has.

I played around with interface and reflect.TypeOf but lost too much performance compared to just storing everything in a string. Strings on the other hand now fill up my memory with stuff I don't need - at least that is my impression.

I don't have programming experiences so maybe I misunderstand the discussion or just overlooked a way to solve my issue in an efficient way. So sorry if the example I mentioned something easily doable in go.


> My goal would have been to have one generic type that can store the cell types text and number which I can store in an array.

FWIW generics wouldn't help you with that, "sum types" would. A sum type is a souped-up enum which can store associated data alongside the "enum tag", so you can have e.g.

    enum Foo {
        Bar(String),
        Baz(u8, u32),
        Qux,
    }
and at runtime you ask which "value" is in your enum:

    match foo {
        Bar(s) => // got a Bar with as string inside
        Baz(n, _) => // got a Baz, took the first number
        Qux => // Got a Qux, it stores no data
    }
(and in most languages the compiler will yell at you if you forget one of the variants).

So for your use case you'd have e.g.

    enum Value {
        Boolean(bool),
        Integer(u32),
        String(String),
        // etc…
    }
> I played around with interface and reflect.TypeOf but lost too much performance compared to just storing everything in a string.

Maybe try a struct with an explicit tag rather than reflection? e.g.

    type ValueType int
    const (
        TYPE1 ValueType = iota
        TYPE2
        TYPE3
        // … one for each concrete type you want to wrap

    )
    struct Value {
        type ValueType
        value interface {}
    }
and then you can

    switch value.type {
    case TYPE1:
        value.value.(Type1)
    case TYPE2:
        value.value.(Type2)
    // etc...
I'd expect reflect.TypeOf to be very expensive, this is a check + a cast.


Thanks for that information. I had thought enums can only be used as named types. Good to know they can hold data as well. I had though of that struct as well (ok, not in that professional way with types as constants and iota:-)) but found it a bit annoying that I have to store a type myself although the type itself should already have the type information somewhere itself - consequently that information is stored reduntantly. But I'll definitely try that. Thanks.


> I had thought enums can only be used as named types. Good to know they can hold data as well.

That depends on the language, and enums being sum types also depends on the language.

* Rust and Swift enums are sum types (they can hold data and every variant can hold different stuff), there is also a ton of (mostly functional) languages with sum types not called enum: Haskell, F#, OCaml, … there are also languages which replicate them via other structures (sealed classes in Kotlin, case classes in Scala).

* java enums (a bare "enum" is the C one) can hold data but every variant must hold data of the same type so you'd need up/down casts

* C++, C# and C enums can't hold data

Go does not have enums.


C++17 has support for sum types via std::variant.


Minor nitpick: case classes in Scala are product types, not sum types. I think you meant "sealed traits".


The combination of both really.


FWIW, "enums" are in most languages you'll run into somewhat distinct from "enum types".


I kind of think the same way, but thanks to Docker and K8s success, it means we might have to deal with Go code regardless how we think about it.


Maybe. My intuition is that k8s is going to lose its luster once people actually have to do a little math related to its costs; while I think there are real reasons for something like it in on-prem environments, I think the cloud-in-your-cloud-so-you-can-cloud-while-you-cloud approach currently being rolled out is profoundly unwise. (Which is to say: k8s is functionally something to weld together to make an OpenStack alternative--such as it is--rather than a layer to plop on top of one.)

Docker...yeah. We're stuck with it there. And their historical security posture doesn't make me super excited, but...yeah.

I hate when you're right. But you usually are.


We have a similar viewpoint w.r.t Higher Kinded Types in F#. Much of the motivation for HKTs comes from people wishing to use them how they've used them in other languages, but we've yet to see something like this in our language suggestions or design repositories:

"Here is a concrete example of a task I must accomplish with <design involving HKTs>, and the lack of HKTs prohibits me from doing this in such a way that I must completely re-think my approach.

(Concrete example explained)

I am of the opinion that lacking HKTs prevents me from having any kind of elegant and extensible design which will hold strong for years as the code around it changes."

Not that HKTs and TypeClasses aren't interesting, but without that kind of motivation, it's quite difficult to justify the incredibly large cost of implementing them well. And that cost isn't just in the code and testing of the compiler. This cost also surfaces in tooling, documentation, and mindshare. It could also have an unknown effect on the ethos of F#, which for better or for worse, does bring with it a bit of a somewhat intangible quality that many people like.

Personally, I think Golang should implement generics. But I'm sympathetic with the views expressed by Russ Cox here. And I don't think it's just sampling bias.


This is apples and hand grenades, though. You're looking for a use case for HKTs. I just want to make a tree without copy-pasting.


Fair point.


This is a fair point. OTOH, just punting entirely seems like the wrong reaction.

Rust, for example, has been thinking about HKTs for a while, and they might not fit well in the language. Rust wants to solve the problems that HKTs solve, and it looks like the solution is converging to ATCs (associated type constructors).

It's taking a while, but Rust is newish, and it isn't taking anywhere near as long as Go is taking to get generics.


It's the same thing in principle, but it's one of those cases where details matter a lot. Generics have been mainstream - as in, implemented in mainstream programming languages used to write millions of lines of production code - for over a decade now. At this point, someone claiming that they need specific user scenarios to convince them that generics are worthwhile, or that they aren't aware of a "good enough" way to implement them, beggars belief.


I think you're mostly right. Doesn't it make sense then to ask for help correcting that bias?


I have used generics for quite long. When I used it I loved it. Then I moved to a different language for a few years, and lost generics. Now I have written large programs in GoLang too - and frankly I do not miss out on generics at all. Now, I also find reading code with generics isn't straightforward either - it requires a slightly complicated mental model.


Yes, using empty interfaces and adding common data structures to the runtime eliminate the primary use-case of generics. Who would have thought..


So what's happening here? The parent made a reasonable comment, without flaming anybody. All his children give evidence for him (except one but they weren't able to downvote) and yet the parent is downvoted. Votes don't matter but I'm curious about drive-by-downvoting. What does that signify? Fanboyism?




Registration is open for Startup School 2019. Classes start July 22nd.

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

Search: