Live data from Hacker News

Channels Are Not Enough

gist.github.com

181–190 of 224 posts

Re: Channels Are Not Enough

#181
post #102

Earlier quoted context omitted.

It was the community attitude towards generics and code distribution that eventually made me focus on Rust and D. At least those communities embrace modern computing abstractions and code distribution practices.

It's fine that you don't agree with our tastes, but it would be nice if you could stop coming into every Go thread and being condescending about it. Like PostgreSQL and OpenBSD, I've been permanently turned off Rust because I never want to be left at the mercy of a community that invests so much time in harassing and insulting others.

We have rules against harassing and insulting others in the Rust community, and pjmlp does not speak for us.

Re: Channels Are Not Enough

#182
post #83
post #21

Earlier quoted context omitted.

That really depends on what you mean by "most of the time". After all, you only write a generics based library once, and you reuse it many times - therefore on the outside it appears that you don't really use generics all that much. In my opinion, depriving a "normal" language user of generics is like depriving a Lisp user of macros. Whatever it is, its definitely not just "bold".

I've done most of my Go programming on library code. A sync library for relational databases to sqlite (for mobile devices) and an embedded database. Lack of generics has bothered me a little, but copy and paste with a multiple-cursor editor really makes short work of it. I think the worst part is really our natural aversion to code duplication and the ugliness of it. e.g. having to write min/max for integers is pret…

OffTopic: This sync library is available?

Re: Channels Are Not Enough

#183
post #85

Earlier quoted context omitted.

And yet, the dmd compiler for D support templates and is faster than the gc Go compiler.

I'll say it again: Go doesn't have generics because of implementation complexity. The tradeoff was against the additional complexity that our users would have to deal with.

I don't know where people would have gotten that idea from: https://golang.org/doc/faq#generics

Re: Channels Are Not Enough

#184
post #156
post #32

Earlier quoted context omitted.

You don't have to resort to an empty interface, in fact, mrust experiences go programmers cringe when they see anyone using empty interfaces. The fact of the matter that much code can reused without being "generic" and often your code never gets reused. YAGNI and all that.

The point of generic code is not purely that it's easier to re-use. The almost more important fact is that generic code has less information about its inputs and outputs—this leads to a smaller design space and, consequently, an easier time designing the implementation and an easier time avoiding bugs.

I think a lot more bugs are introduced from people trying to make their code too generic rather than too specific. The complexity of the code goes way up when you stop being able to rely on certain values being certain types. If I "hardcode" my ID to be an int64, there are a lot of assumptions that become safe to make, like the fact that it's a relatively small value, that I can bit shift it, that the keyspace is of a particular size.... if you then take the same code and decide that the ID should be genericized so it can be anything, now I have to account for the fact that the keyspace could be unbounded (as in the case of a string ID), I can't assume it's safe to copy around the value (it might not be thread safe and/or it might be a really large value).

Saying having less information about the data makes for easier coding is almost always not true.

Re: Channels Are Not Enough

#185
post #71
post #32

Earlier quoted context omitted.

You don't have to resort to an empty interface, in fact, mrust experiences go programmers cringe when they see anyone using empty interfaces. The fact of the matter that much code can reused without being "generic" and often your code never gets reused. YAGNI and all that.

It would be really useful if Go allowed you to define methods against types imported from other packages. That way, you could define whichever interface you needed against those types (using only its public API, of course) and then use those interfaces for collections, generic functions, and the like. The closest I've gotten to that has been to create a single-member wrapper struct. Go provides a little bit of sugar…

Simply embedding the type and writing whatever additional methods you need is actually incredibly easy. The boilerplate beyond what is required to actually define the new functions is really tiny

    type Foo struct {
        pkg.Bar
    }

    func UseIt(b pkg.Bar) error {
        return otherFunc(Foo{b})
    }
I think that's actually one of the places Go works really well. It sounds like you want something like C#'s extension methods, which I don't think are a good thing (I used them a bunch in a past job). The problem with them is that it means your code can spontaneously and mysteriously break if you move it somewhere else that isn't including the project that has the extensions. Extensions seemed nice, but they really only made the code a tiny bit cleaner, and the added complexity did not really make up for it, in my opinion.

Re: Channels Are Not Enough

#186
post #177
post #11

Go doesn't let you build abstractions - it offers what it does, and if its not enough - tough luck. What I dislike worst is the denial of the Go community and creators, claiming that generics are too complex and that you don't really need them. I dismissed Go not because of its lack of abstraction power, but because its authors and community is incapable of admitting problems when they see them. A similar problem wit…

I also find Go pragmatic but lacking abstraction power. The other day I needed a stack, and the best solution I could find was this: https://groups.google.com/d/msg/golang-nuts/iwlzqNa4h3g/xCy3... to be honest, if i need a stack, i usually implement it for the type in question, or inline, as it's only a very small number of lines: type stack []T func (s *stack) push(t T) { *s = append(*s, t) } func (s *stack) pop() T…

It's 10 lines of very obvious code. Isn't that a good thing? Do you really need stack.New()?

Re: Channels Are Not Enough

#187
post #156

Earlier quoted context omitted.

The point of generic code is not purely that it's easier to re-use. The almost more important fact is that generic code has less information about its inputs and outputs—this leads to a smaller design space and, consequently, an easier time designing the implementation and an easier time avoiding bugs.

I think a lot more bugs are introduced from people trying to make their code too generic rather than too specific. The complexity of the code goes way up when you stop being able to rely on certain values being certain types. If I "hardcode" my ID to be an int64, there are a lot of assumptions that become safe to make, like the fact that it's a relatively small value, that I can bit shift it, that the keyspace is of…

Ideally, the language won't permit you to make such assumptions without making them explicit. In practice, every language falls short of that, and many implementations of generics fall far short of that. Note, though, that manually implemented generics - copy-pasting and changing what you have to - isn't actually any better in this respect.

Re: Channels Are Not Enough

#188
post #156

Earlier quoted context omitted.

The point of generic code is not purely that it's easier to re-use. The almost more important fact is that generic code has less information about its inputs and outputs—this leads to a smaller design space and, consequently, an easier time designing the implementation and an easier time avoiding bugs.

I think a lot more bugs are introduced from people trying to make their code too generic rather than too specific. The complexity of the code goes way up when you stop being able to rely on certain values being certain types. If I "hardcode" my ID to be an int64, there are a lot of assumptions that become safe to make, like the fact that it's a relatively small value, that I can bit shift it, that the keyspace is of…

> decide that the ID should be genericized so it can be anything

You say that, but you probably don't actually do it. If it truly could "be anything" then you would not be able to do anything to it. This is the nature of polymorphism.

So instead, your algorithm constrains the polymorphism based on what you need. If you make use of the properties {small, bit-shiftable, sized, bounded, copy-safe} then you must prove (exactly and only) that whatever the generic variable gets instantiated to satisfies {small, bit-shiftable, sized, bounded, copy-safe}.

Indeed, this is part of how such a system prevents bugs---it forces you to express exactly how much information about the types involved is needed. You can thus reflect better on the kinds of contracts/laws things must uphold and are prevented from accidentally making use of a property of your concrete type which you do not demarcate.

In a truly expressive language you might write

    foo : forall id n . 
          (Bits id, Size id = n, n  id -> {Copy id} id
to indicate all of those properties needed (bounded being subsumed by constraining the size)

Today, you can get promises very similar to the above by using a language like Cryptol[0].

[0] https://galois.com/project/cryptol/

Re: Channels Are Not Enough

#189
post #8

Earlier quoted context omitted.

How about replacing certain compile time type checks with smoke tests and runtime assertions, or unit tests?

Why do what the compiler can do for me?

Because you like other features of the language and you just want to get on with it.

Re: Channels Are Not Enough

#190
post #158

Rather than arguing about generics in Go again, I'd be interested in reading about how experienced Go developers solve the problems posed in this article. (He may be wrong that there's no elegant solution.) Also, if it can't be solved elegantly, perhaps adding merge() and a few other important functions to the language would be good enough? After all, Go already has the magic append() function for slices and we get q…

There's no generalized solution to every generic problem. The "cast to interface{}" is the closest to a linear mapping of generics, but if you're reaching for that all the time, you're doing it wrong. So the only sane answer is, "it depends". The only use case that I have encountered that is essentially impossible in Go is the "generic data structure". Mind you, that's a bit of a big deal, even if it covered over by…

Talking about how to implement generics generically isn't really responding to my question. The original post had questions about how to implement specific concurrent constructs like pipelines, futures, and so on. So the question is how do you handle concurrency in Go? Are we missing important features that should be part of the language or standard libraries?

Some of these may be one-off generic functions like append() that could be added to the language without adding full-blown generics.

Post reply on HN