Toward Go 1.3
talks.golang.org
talks.golang.org
I'm surprised at the lack of progress in generics for Go. But more than progress, I'm surprised at the lack of a story about generics in Go. Yes, the FAQ waves its hands at complexity, but the lack of discussion and/or proposals puzzle me. The wiki page about it (https://code.google.com/p/go-wiki/wiki/GoVsGenerics) is tiny and feeds from this discussion (http://groups.google.com/group/golang-nuts/browse_thread/thr...). I would be much more interested in Go were there some evidence for the intent to implement generics. A related concern is that adding generics will have a significant affect on libraries and existing code, so adding generics will become harder the longer Go waits.
I would be very happy to have the Go team say: we're going to focus on adding generics to Go in 2.0 and will be considering how to get there sooner than later; that said, we don't know when 2.0 will be released, but building in generics will drive 2.0.
Note: I understand the workarounds, but they're either hacky or have terrible performance. And I also understand that users of Go say that they don't miss generics, but I'm just not comfortable believing that.
I'd like them in part because it'd enable all kinds of delightful reactive functional idioms that just aren't very possible currently. On the other hand I'm accomplishing an awful lot in Go without them..
Link?
Missing generics does not mean that no alternate data structures ever get written; as you can see, there are three of them there, which is greater than zero. However, it does mean they are more painful to use, consistently, everywhere, and no matter how much we'd all like to pretend we are infinitely disciplined programmers, pain matters, and in practice programmers visibly shy away from harder things. It is simply a fact that we must deal with. Generics missing from the language are a problem because it means that doing the right thing is harder than doing the wrong thing of just, say, using the wrong data structure because it's more convenient. A language (or an API or a library) should make right things easier and wrong things harder; it is a legit criticism when the right thing is impossible and the wrong thing (extensive copy & paste) is the only choice.
Maps & slices are great and all, but they do not correctly cover all use cases, and the sort of servers that Go is otherwise very suitable for is also exactly where you might end up needing something other than those two things for performance reasons.
I'm living without generics in Go, of course, because everyone programming in Go is. But I definitely see a lot of places here and there where I'm writing 5 lines extra, 10 lines extra, here, there, a little bit everywhere, that I simply should not have to write. Or, far worse, debug. Too often in Go I'm copying & pasting code where I should have generics, too often I'm fiddling with data structures at a lower level than I really need to.
My concern is not nomenclature, but that correct code must handle paniceptions. And that it must also handle return value errors. Go code IMHO ends up spending a lot of code on error handling, I think because of this double taxation.
This won't unlock the mutex on panic, which is observable if someone is trying to recover():
func F() {
mutex.Lock()
... do something here that panics ...
mutex.Unlock()
}
But this will: func F() {
mutex.Lock()
defer mutex.Unlock()
... do something here that panics ...
}
This is basically the same set of hazards as maintaining exception-safety in C++ or Java. So in this regard panic is very much like an exception system. (Of course, it has very different idiomatic use.)You should probably be using defer() all the time anyway, unless you have a good reason not to. It also helps with code evolution, in the cases where some yahoo adds a new return statement in the middle of a function.
try {
... do something that throws ...
} catch (...) {
... deferred code
}
... other code
If you don't use `catch`, then I could agree that try/finally is the same as defer, but I would argue that defer is a much cleaner design since cleanup code is located next to the thing they're cleaning up.I think it's much easier to audit this:
mutex.Lock()
defer mutex.Unlock()
Than this: mutex.Lock()
try {
... code that panics
} finally {
mutex.Unlock()
}
Other languages also make a distinction here, for example Python's `with` and D's `scope` [1].Also, it's trivial to make a `panic` that is unrecoverable: `go panic("broke your code, lol!!")`. This just cements the idea that `panic` is semantically different than exceptions, and should be treated as such.
You can do that by creating another function.
> Also, it's trivial to make a `panic` that is unrecoverable: `go panic("broke your code, lol!!")`. This just cements the idea that `panic` is semantically different than exceptions.
That's not different. In, say, Java, you can set the default uncaught exception handler to get the same behavior and then you can write:
new Thread() { throw new RuntimeException("..."); }You can't emulate the behavior of continuing the current block after an exception is caught. You have to recover() and copy/extract into a function any code that you'd want to run in the recover.
For example:
try {
... code that throws
} catch {
}
... other code
In Go, to run "other code", you'd have to duplicate all of that logic in the recover(): defer func() {
if err := recover(); err != nil {
... other code (duplicated from below)
}
}()
... code that panics
... other code
This isn't really the same thing, but I suppose you could technically get the same effect if you move all of "other code" into a function and called that in both places, but you're still duplicating code.Panics and exceptions are two very different things, which is why there are different idioms in place to make working with them safe.
Right. It's a pretty trivial transformation, and that's why it's not inaccurate to call panic/recover equivalent to exceptions: you can straightforwardly express every exception-based pattern using defer/panic/recover, and also the other way round. Sometimes you have to make more functions to make panic/recover work, but that's part of the "tied in with function declarations" nature of panic/recover/defer—there's nothing semantically that deep about it because the transformation is still quite simple.
Anyway, I assume you're the same pcwalton from Rust? I really like the design of error handling so far, especially the bit about trapping conditions. From what I read, it looks failure just kills the task, instead of the entire program (like it does in go if unhandled).
I'm really looking forward to 1.0. Keep up the good work!
No, correct code doesn't have to handle panics. Panics are for programming mistakes.
Without defer, to be correct you would have to explicitly 'recover' (and re-panic?)
You seem really hung-up on this point. Can you link to some code that illustrates your concerns?
It's even used in the standard library: http://golang.org/src/pkg/text/template/exec.go#L93
(I use the pattern myself in certain situations. It's extremely useful.)
I have exactly two panics in my ~15k line server. Both are in initialization code that will probably never get called, so it will fail very early on in the code. The rest of my code looks like this:
func getRecord(args...) (err error) {
if err := doSomethingRisky(); err != nil {
return err
}
if err := doSomethingElseRisky(); err != nil {
return err
}
... other code ...
return nil
}
func processRecord() error {
if err := getRecord(args...); err != nil {
return err
}
... do other stuff ...
return nil
}
All the way down the stack. It's certainly a little more code, but it forces you to at least acknowledge all errors. If you want a stack-trace, you can always use the runtime package.I'm not talking about panicing instead of returning errors. I'm talking about using an idiom---which is used in the Go standard library (see my link up-thread)---to make error handling more terse when you're working with code that is otherwise profligate with checking errors.
At no point is a panic exposed to the user of a program or to the client of a library. At no point are errors ignored. The panics are kept within package and converted to error values.
Without seeing specific code I can't say for sure, but it's very unlikely that any database interaction code is best modeled with panic/recover for error handling. I'm very curious to see the source, at this point.
Using panic/recover doesn't automatically make your code nonidiomatic.
> (That the stdlib uses panic/recover in a few specific places does not make it broadly idiomatic.)
That the stdlib uses panic/recover in several places is a good indicator that "never use panic/recover" is bad advice. Note that while I agree that just because something is in the stdlib doesn't mean it's idiomatic, I also cite that this particular approach is used to make the structure and organization of code more clear. Since it's used in several packages, I claim that this is a strong hint that panic/recover is appropriate in limited scenarios.
> Without seeing specific code I can't say for sure, but it's very unlikely that any database interaction code is best modeled with panic/recover for error handling. I'm very curious to see the source, at this point.
It's really not that hard to imagine. For example: http://play.golang.org/p/fhpRLd8EHY
We seem to have some wires crossed. Let's be clear, shall we?
* The panic/recover idiom is rarely used, but it is an idiom.
* There are trade-offs involved with using panic/recover. In my sample linked in this comment, many of the functions in database/sql need to be stubbed out so that they panic. However, the cost of this is relatively small, since it can mostly be isolated in a package.
* The idiom is most frequently seen in parsing because there are a lot of error cases to handle and the response to each error is typically the same.
* While parsing is the common scenario, I claim it is not the only one. I cite that database work is profligate with error checking, and depending on your application, there's a reasonable chance that the response to each error is going to be the same. When doing a lot of it, it can pay off to use the panic/recover idiom with similar benefits as for doing it with parsing.
* There may well be other scenarios where such handling is appropriate, but I have not come across them.
I've done an unusual amount of work with parsers and have done some database work, so I've had the opportunity to bump up against the panic/recover idiom a bit more than normal. As with anything else, it can be abused. But I find it extraordinarily useful in certain situations.
* generic functions (e.g. append)
* generic data types (e.g. slices)
* exceptions (like illustrated above)
* special case syntax
* Custom event loops
* precompiler macros (very bad to use, horrible, blah blah ... except of course for the people imposing this restriction, and YES they're using it amongst other things to workaround the lack of generics in C)
...
This attitude was common in middle-90s "generic" programming languages like Ocaml, Modula-2 and others. You should simply look at Go as one of those languages and treat it as such.
If this attitude bothers you, you should look at C++0x and D.
The canonical examples are closing a file and releasing a mutex. Both have code samples here: http://blog.golang.org/defer-panic-and-recover
I'm confused as to what you guys are saying: are you saying that you don't need to handle exceptions (whether using defer or recover), or that it's better to use defer over recover? I take exception to the former, totally agree with the latter.
> are you saying that you don't need to handle exceptions
> (whether using defer or recover)
Go doesn't have exceptions. You don't need to handle (i.e. explicitly deal with) panics via recover. If you do, especially if you're not making the panics yourself in e.g. a parsing package, that's a bad code smell and you're probably doing something wrong.If you do still think I have gaps in my knowledge, I humbly suggest that you briefly fill in those gaps with facts; it should save you time in the long run and will likely win you a few converts!
Defer is primarily used to make sure that clean-up happens in functions that have multiple exit points (return statements). It's a convenient side effect that defers are executed while a panic unwinds the stack, but it is rarely the first thing on the mind of the Go programmer when they type "defer".
Embrace error values. Return them! Check them! Panic when shit goes really bad. That's it. If you're writing Go code and you're thinking about "throw" "catch" or "finally", you're doing it wrong. Go's features do not map cleanly to those concepts, because Go doesn't have exceptions.
At least in my world, every time I type "go", I must ensure that I am starting a goroutine that has some sensible top-level recover mechanism, and, honestly, for any Go program that actually plans on using concurrency, I think there's no alternative. You MUST handle panics. Why? Because an important aspect of Go's concurrency is maintaining composition of independent processes, and there are few things more uncompositional as a completely unrelated computation in a completely unrelated thread that trashes your entire OS process.
Panics may be for programming mistakes, but for any non-trivial code, you have some. Hopefully you can work out a better way of handling them than completely bringing the entire program down.
I am willing to assert that my code maintains enough isolation that continuing on is a reasonable thing to do. (It's a port of Erlang code anyhow. This is not a very freaky claim about such code.)
I am also not a fan of hyperliteral case-by-case error checking (it reminds me of my early C code), but it's A Style, one that the Golang team enthusiastically adopts, and it's unlikely to go anywhere.
But correct code must assume that any function you call might throw; which is why you should use defer blocks e.g. to release resources, instead of C-style "cleanup at the bottom of the function". (Defer is also prettier IMHO)
The execution is suspended, the stack unwound until the first handler, the handler has access to a value that is "thrown". Stack information is preserved in order to print meaningful stack traces.
C++/java/python have syntax sugar that performs a pattern match on the thrown object to decide whether to handle it or bubble it up, while in Go you do it manually, but other that that I don't see much of a difference in the mechanics of them to justify being so pedantic about the naming of the action.
They are used for things like out of bounds indexes, where in C it would simply be a segfault. A panic is a way of gracefully exiting a program that would have segfaulted otherwise. Correct code should check for out of bound indexes either way.
Though you raise an interesting point: Should you check array indexes if the runtime is also checking it for you?
In Java, the runtime is guaranteed to throw an exception and it is relatively rare that you would pre-check the array indexes (you might use assertions in debug code).
Incidentally, array bounds checking is actually relatively expensive, to the point where most JVMs (which use signed indexes) use an unsigned comparison trick to make it one comparison instead of two. So it does matter...
Except you don't. I haven't used recover in any of my code for a long time (more than a year). Most of the time you don't need to worry about handling panics, but you can if you really need to.
> Should you check array indexes if the runtime is also checking it for you?
In Go the generated code does it. You shouldn't do it yourself.
We're seriously drifting off track here, but if we should rely on Go to check array indexes, that seems like you _would_ want a recover block, so that we can map it to a Go-preferred error code?
There's also no such thing as a "recover block" (you're thinking of a "finally block" or "catch block", neither of which exist in Go).
If we thought you should use recover any time there might be an array out of bounds panic, we'd have designed the whole language differently. Panics should happen when things go badly wrong, and most of the time that means your program should crash.
You should use recover only in two rare cases: 1. where you're specifically using panic/recover as a kind of setjmp/longjmp (as it is used within encoding/json, for example), and 2. where you don't want a programming error to bring down your entire program, such as in the base net/http handler (although I think it's debatable whether we should have done it there; but it's done now and we can't change it).
It amazes me that there has been so much discussion over this incredibly minor and seldom-used feature. Just return and check errors (and just panic when things go really wrong) and get on with your life.
Of course this "pattern" should only be used when you're sure that that one failing goroutine won't have a cascading impact on other goroutines that are still running.
As opposed to an error, which can and will happen.
More than just the normally used: if err := foo(); err != nil; { return err }
Large code Go code basis are so much better (more maintainable) for "hyperliteral case-by-case error checking". My impression that only people who havn't written large Go projects have this complaint.
Also, there is no reason you could not define a function like:
func foobar() (fatalErr, err error)What I was showing was that:
try {
foo()
} catch (ex1 ExType1) {
...
} catch (ex2 ExType2) {
...
} catch (ex ExBaseType) {
...
}
Type logic is completely possible, which while apparent to you is lost on some people who haven't work with non exception langs before.Underpowered error handling (and I'm not advocating for exceptions, persay), lack of generics (and the resultant copypasta party and interface{} runtime casting [aka, the return of void *]) are real warts in an otherwise fine language.
And I'm not just theorizing: I spend my days writing a large, nontrivial system in go.
I've used Haskell a lot before, and I'm not asking for no nulls or real ADTs (though I wouldn't complain), but generics + better typed errors would really help clean things up.
Meanwhile, a lot of us are just waiting for rust...
It just depends on what you're trying to accomplish, but most use-cases can be accomplished without exceptions. The other use-cases often indicate bad design.
As for generics, you can get 90% of the way there with interfaces. The use of `interface{}`--while sometimes necessary--is often an indicator of bad design.
In large code bases, you often don't need (or care) to know what underlying type something is. For example, you shouldn't care whether an `io.Reader` is a TCP socket, file or completely in-memory ala `io.Pipe()`.
There are times when type assertions are the best/only way to get something done, and that's why they're there, but those cases should be relatively infrequent.
Generics would make some things easier (Rust's implementation is quite nice), but it's not significantly impacting my productivity, and I certainly wouldn't consider switching languages just because Go lacks them.
EDIT: Added info about generics
type URLError {
url String
}
func (e URLError) Error() String {
return fmt.Sprintf("Invalid URL: %s", e.url)
}
At which point a caller that wants to handle this error can either extract the URL to do fun things with it or just dump the Error() string.This is a widely used idiom in Go.
object NonFatalError {
def unapply(err: Throwable) = err match {
case _: TimeoutException => Some(err)
case _: IOException => Some(err)
case _ => None
}
}
def executeWithRetries[T](maxTries: Int)(callback: => T): T =
try {
callback
}
catch {
case NonFatalError(error) if maxTries > 0 =>
logger.warn(error)
executeWithRetries(maxTries - 1)(callback)
case other: Throwable =>
throw other
}
And usage: def funcMayErr(): Int = throw new TimeoutException
val value = executeWithRetries(5) {
funcMayErr()
}
But there's more. Because with generics and exceptions you can actually wrap the whole result in an algebraic data-type that also has handy methods for dealing with failure (e.g. a monad), as in: Try(executeWithRetries(5)(funcMayErr)).map(x => x + 1).getOrElse(default)
Cheers,That it can abstract some other "common patterns" doesn't solve this.
can easily be implemented in Go: http://play.golang.org/p/kMNqfY7LYX
* those casually glancing over to figure what the code does * those actually trying to figure out what a given expression does, either because they are reviewing or debugging * compilers are people too
Conciseness is often overvalued and pursued to the extreme where effort is made first by the author to seek for the perfect oneliner, and then for the reader to actually check that this code is doing what expected.
Composition is important, but I don't think I found great real world examples of composition which wasn't either working only because of a tightly controlled code base or because it was just an example to prove a point.
Don't get me wrong, I love scala/haskell, I find playing with those constructs interesting and beautiful.
It's just that Go is a different thing, is a modern approach of getting back to basics, a minimal toolset for do just programming, more or less translation of thought into instructions.
And it's works very well; it's very easy to get things done quickly and the produced code tends to be easily maintainable. It's easy to have control over the memory footprint. The tooling is very mature (http://blog.golang.org/race-detector, gofmt formatting+refactoring)
Why do people keep ignoring me when I say this? I guess the idea that the Go team are a bunch of generics-hating curmudgeons is more compelling than the reality.
However, I'm taking into account you said that they do :)
I don't blame you for not writing it up, I don't think I would either ;)
Golang doesn't have to be all things to all people, nor does it have to fulfill every programming task any one developer has.
I would be just as happy at this point to see the Golang team put generics to rest. And I like generics, and have been annoyed by their absence in Golang before!
This. The same can be said for C as well, but C is not memory safe and Go is- so it wasn't immediately obvious the same would hold for Go.
I used to use generics/templates in Java/C#/C++ a lot, and at first I missed them in Go, but now, after writing Go full time for 2+ years- I no longer miss them more than once or twice every few months... I think generics are great, but also greatly overused.
It's boilerplate, which generic methods would avoid.
There are shortcuts for sorting common types by their natural order in Go, so you only have to write this for custom types or custom orderings.
Could you explain why Go needs an interface with 3 methods?
Go has no such "blessed" precompiler-macro system.
Templates/macros are an ugly solution, but they are a solution.
So yeah, the toolset may not have anything like the C's preprocessor (the lack of which is a blessing, not a curse, IMO), but that's fine, if you're sure you really need such a thing just spend half a day writing your own in Go that is specific to your project's needs and make that a part of your local build process.
No real tooling either other than whatever you choose to use for the build process. For the projects I've done this for that ends up being a very simple Makefile which does a 'go run' on the code generation engine code prior to a 'go build' of the resulting combined package of generated & hand-written code.
[1] http://clipperhouse.github.io/gen/ ("A library for bringing generics-like functionality to Go")
I've never used gen, it looks pretty nice though, at least based on the linked docs and it is probably a good answer to Pxtl's question about publicly available examples.
Tangential, but a decent example of that sort of thing, a generic red-black tree written entirely in the C preprocessor: http://www.canonware.com/download/rb/rb_newer/rb.h
This should be a book or a blog. Seems like the most powerful features of languages are often the downfall of a project. Proxies based on #doesNotUnderstand: or method_missing can be awesome and they can also be misused to render a project incomprehensible. The same goes for macros in C, macros in Lisp, and generics.
Generics makes code far more readable, maintainable and testable.
Comprehending other people's non-generic code is a giant pain in the ass compared to reading code with generics.
The method was named something like "getCollectionOfSomething()"? If so, you might want to put some of the blame on the method naming choices.
Even if you do have a sensible naming scheme that makes the return type clear, it's rather beside the point. If I have a choice between relying on coding convention or generics, I know what option I'm going to pick.
But imho the method should not be named for the compiler-type of it's return value (unless it's a type converter like int2string or similar).
It should be named for the bizlogic/semantic type:
So..not "getURIs()", but better "getBackendPoolMembers()" or "getBackendPoolMemberURIs()" if you have more than one way of representing them.
Long method names are fine. Everyone uses autocomplete. Choose good names and rename them often as the meaning of the code changes over time.
var stuff []MyType
>I would be just as happy at this point to see the Golang team put generics to rest.
Clarity on this point would be great. And update the FAQ.
As it is, Rust is looking better and better as Mozilla tightens it up.
EDIT: I added the Rust note after tptacek added his reply. Apologies to him.
If it's not speed of execution then what makes Go a good systems language? The ability to fiddle bits? A small runtime?
One thing I've been enjoying out of the Go community is narrowly scoped command line tools. Heroku's CLI client runs on Ruby, but there's an `hk` variant written in Go that offers 90% of the functionality with a fraction of the runtime. More of that, please!
Go is actually quite well suited to GUI code, because it handles concurrent work very easily (i.e. work on the gui thread vs. work on a non-gui thread).
About the only thing you can't do nicely in go is write a domain specific language, the way you can in other languages that allow operator overloading.
Anything you might think about writing in Java or C# you can write easily in Go (once the GoQML stuff is ready that'll include GUI code). Most things you might want to write in python or ruby you can write easily in go (as long as you don't need monkey patching).
And note, I mean "Would be pleasant to write in Go" not "Would be possible but painful to write in Go"
If you're doing a lot of vector and matrices math, Go might not be the best language, since you can't operator overload and thus can't make the code look like the math it's trying to replicate. It would still work and would still be fast and accurate and nicely concurrent... but most people doing that kind of math programming have come to expect that parts of their code will look like math, and in Go it won't.
Sure generics would be nice, but getting my shit done and moving on to the next challenge is even nicer. Hopefully some carefully thought out additions to Go will allow me to have all this goodness at some point.
Sorry for drift, but I have to put in a word for formalized pre/post conditions in Go. Really enjoyed that stuff in Eiffel, and I think it would play in well with the judicious lack of exceptions (which I like).
are you exaggerating here? C# is already the most productive language in my toolkit. It's easy to write reams of code that just works in C#. I should have a look at Go then. (edit forgot a word)
EDIT: I guess you are saying something slightly different: that you can produce reams of C# code that works out of the box without many iterations. "reams" to me implies boilerplate & excessive ceremony, but I shouldn't assume that about your work =)
http://fpbridge.co.uk/why-fsharp.html (checkout the call graph comparisons. I believe a Haskell call graph would be similar, but I could be very wrong.
What language feature(s) would you say greatly assisted you in Go?
There's a credible argument that C# is a very complicated language (it is relatively old and has undergone a significant amount of iterations, thus is quite large), but I don't usually find that programmers are defining their own subset of the language to program in (a la C++), which usually means that the complexity isn't that large for me.
If you have any specific complaints I would love to know them as C# is being actively developed right this second, including new language features (well, not by me, I'm on vacation :)).
If you feel that HN is the best forum, please feel free to forward them to the email in my profile.
I thought the whole point of picking a language like Ruby was to use a language in which you can describe what you want, instead of how you want it.
Boy oh boy do I ever miss collection-level constructs like map and reduce in Go. I just don't think of them as 'generics' because I'm not very well-versed in statically typed languages.
I still bite the bullet and use Go anyway when it feels appropriate, because I can get fast type-checked programs with a reasonably quick prototyping cycle.
Plus after you get types down well enough, you can largely pretend you are using a dynamic language. For instance sometimes I write my functions, make sure they work right, check the type ghci says they are in the repl, and add the type annotation.
The need for generics (or macro/template-based equivalents) naturally falls out of using a statically-typed language.
> a variety of complex programs are being built and deployed in Golang without generics, which makes it an open question as to whether they're required at all.
And a variety of complex programs have been built and deployed in assembler. There are arguments against adding generics, but the fact that people get by without them isn't a convincing one.
Perhaps those people working on it can give an update or some info on their work? Even a "we tried this approach, it's too cumbersome so we abandoned it" or "we pursue this idea, but we're still ways off" etc, would suffice.
No one owes you anything here. I hope you don't behave this way with other open source projects.
Anyway, just consider what their behavior says about how they think of their users.
Should I just accept the same kind of answer we've had for 4 years on the Go webpage, and move on?
You talk about wanting "actual proof" as if it's a bad thing. Where's the harm in wanting "actual proof"? Did Ken Ham proved it overrated?
Notice how the top voted comment in this thread, showing a clear community interest, makes the same point:
"I'm surprised at the lack of progress in generics for Go. But more than progress, I'm surprised at the lack of a story about generics in Go. Yes, the FAQ waves its hands at complexity, but the lack of discussion and/or proposals puzzle me."
>No one owes you anything here.
That doesn't preclude discussion, having questions and, yes, even doubting overly generic answers. Noone owes anything to the Python community either, but they are far more open with such things.
Perhaps I want to know if Go wants to be a real community project, or a "developed by a core team at Google in secrecy you get what we want you to get when we want you to get it" thing.
Yes. Clearly its time to find another language that better suits your needs and is managed more to your liking.
Isn't that like a really inadequate answer to people trying to critisize/improve things? I sure get the rednecky "If you don't like this country mister, then go live somewhere else" vibe from it.
Clearly the fact that you are satisfied with Go doesn't stop you from inteferring with people having discussions about its (perceived) shortcomings.
So why should my disatisfaction with it prevent me from discussing it Especially if its a dissatisfaction with some aspects of it, and not the whole thing? By the same logic, nobody should use anything (nor complain about anything), since all languages have shortcomings that they don't like.
Anyways this is a pointless argument. I'm sure youre a great guy in person.
Well, we appreared as representing the Go team, and then rehashed the old "we're actively looking into it" response. I was not trying to be a dick, just wanted something more concrete than that.
Plus, I felt like he contradicted himself, when replying to another comment and said something to the effect that they didn't do anything about generics because they are busy building other things. So I had to ask, which was it, were they actively looking into it or didn't have time and had it as a low priority?
I don't see that in real life. That's just a "shut up" story employed like 4 years ago. If it's an "ongoing research" project, where's any researchy output?
"Python 3000" was once a research project and had tons of proposals and research done on various features. Closures in Java were researched, and there were implementations tested. ES6 was an ongoing effort to add features to Javascript, and there have been test implementations, shims, etc before it's out.
All languages that wanted to add a feature had attempts by various key developers to add these to them, proposals, papers, testing, requests for comments, etc. Where are those regarding Go and generics?
We're not university professors. We're not publishing papers or other "researchy output". We're just trying to get stuff done. If we publish all the works-in-progress we will distract the community from the more pressing concerns of today's Go programmers. We all already spend way too much time reading mail, and generics is a hot topic (obviously). The last thing we need is more (redundant) mail.
Well neither is Larry Wall, or Guido (well, is not working directly as such), or the Rust guys. But they still manage to put out proposals and describe their approaches to adding features, their progress and such.
I didn't mean "researchy output" in the academic sense. Just the results of looking into it, basically. Could be just a blog post. Or some email.
Similarly, a variety of complex programs are being built and deployed in [any other language] without Go, which makes it an open question as to whether Go is required at all.
My point isn't that Go isn't needed. It's that this argument against generics in Go is hypocritical and contradictory to the whole reason Go exists.
The same argument could be used against adding closures to Java. After all "complex programs are being built and deployed without them". And yet, everyone thinks that it's a good idea that should have happened years before.
I think the argument is a variation of "I can do stuff in assembly just fine, why I'd need a higher level language?".
>Golang doesn't have to be all things to all people, nor does it have to fulfill every programming task any one developer has.
Well, generics are pretty basic to talk about having them as "being all things to all people". It's not like the majority of users testing or using Go regularly ask for all kinds of exotic features. The vast majority, judging from the mailing list, blog posts, articles etc, just have this major gripe.
Then, once the GC is decided and realized, wouldn't you rather have Go self-compile so that the community could sprout experimental branches to test out different Generics approaches and designs? Especially since the Gophers themselves aren't too sure about the right approach here and want to get as much feedback as they can...
TL;DR: It seems to me that the current trajectory of focusing primarily on the GC and self-compiling is the right way to go.
I wouldn't say there has been a "lack of discussion" on generics at all..
There's actually a sort of workaround that I kind of liked:
http://play.golang.org/p/LkVWkyph75
Discussion here:
https://groups.google.com/forum/#!topic/golang-nuts/UyKree3B...
The basic approach is to define most of your generic data structure to use interface {}, and then have type-specific get/put and maybe comparison. This then minimizes the boilerplate code needed while retaining type safety. Implementing the generic functions requires a little more care than usual. But I think it is one of the best approaches for the language currently.
It's better these days, but for a while it was a common point of contention.
We only get hand-waving from the Go core devs.
This presentation also doesn't mention the concurrent GC sweep work: https://codereview.appspot.com/46430043/
http://golang.org/src/pkg/net/http/transport.go#L408
A connection pool is kept in the transport for use with HTTP keep-alive. When an http.Client makes a request using the transport, it tries to get an idle connection, failing that it starts creating a new connection. It then selects the first available connection, either the newly-dialed one, or one that becomes available in the pool. (If the latter case, it continues opening the new connection and then sticks it into the pool.)
What makes this implementation special is that its pooling is both thread-local and global, and is plugged into the runtime/garbage collector. The former makes it much much faster than any pool the user could implement, and the later means is plays nice as resources become more scarce.
I actually have improved performance of js code significantly using pools to discard/reuse object instances in js.
(Go's implementation is potentially more efficient as you're not creating the extra WeakReference<> object, as you would be in java).
I would assume that the NaCl support means that we'll be able to write things for Chromebooks relatively easily?
There are generic lists (slices & arrays), there are generic maps (dictionaries). You can build a hell of a lot on top of that.
To generalize logic, Go's interfaces are amazing.
There are some huge projects out there using Go, and not hurt by the lack of generics. Perhaps you've heard of Docker?
I work on Juju[1], a Go project that is over 200,000 lines of code. There's only a handful of places where generics would have made things a little easier, and it's never been "OMG this is so awful because we don't have generics".
And this is coming from a guy who spent his first 14 years programming in C++ and C#, so it's not like I'm not used to generics. You just write code in a different way. Sometimes you can't get around it when you need a containery class, so you use interface{} and cast the result, but that's the exception rather than the rule.
(NaCl supports 32-bit ARM, but we have no plans to support it.)"
epic facepalm
In any case, I'm sure ARM support will be added in time (just not for 1.3 release) if not from the core team than by outside contributors. There are quite a lot of people using Go on ARM devices.
Ideally the running apps would be sandboxed so they could be served from potentially untrusted sources and not do damage to the underlying OS or cause problems or privacy issues with other little apps that would rotate through running, so a framework like the one used as the basis for the Go playground would be ideal.
This is admittedly a very niche need and can be tackled by other OS-level sandboxing techniques (though at that level the support offered by different combinations of embedded Linux ARM ports and/or ARM CPUs makes things more complicated), but NaCL support in Go/ARM would be very convenient here.
This project aside, though, there were lots of listed plans for Go 1.3 in the presentation that I would prioritize way over ARM/NaCl.
Sorry that we don't support NaCl/ARM code gen. Our use case (the Go Playground) only required amd64 support, which is just 386 code gen plus more hard bits. I'm sure if it were straightforward Russ would have just done the ARM port too, but as you probably know ARM is very different to x86 so it would have been another project entirely.
See my comment https://news.ycombinator.com/item?id=7219792
The 'android/arm' mention in the presentation refers to writing native Android apps in Go.
You can check out the Makefiles in libtorrent-go[1] and torrent2http[2]
Aww. Transliterated C code does not sound like it will take full advantage of Go idioms. Plus, an automatic translator? Unless it is extremely disciplined C code, that sounds harder than just translating it by hand.
Doesn't really change the point, of course. :)
It's the only way you can port without halting ongoing development. Done it. Been paid for it. It works.
EDIT: Also, if your original is highly idiomatic, and you exploit this, you can produce idiomatic code in the target language.
Did you work on Go's translator or a different translation project?
This is the most exciting part for me! Although I wish they didn't skim so much on the technical detail here.
I'm not intimately familiar with Go implementation, but I assume that they haven't bothered implementing precise stack- and register-scanning, which means that the runtime cannot distinguish pointers from integers on the stack/in the registers. That's a really hard problem in general and solving it requires quite some compiler support and runtime overhead. It looks like that is finally planned for Go 1.3, though.
Does this also mean that GC times will likely be longer in Go 1.3? There is not much detailed info about the Go runtime (like we do have for Java for instance) so it would great for the Go maintainers to publish a more in-depth reference of the internals of the language in version 1.3 /
To clarify, Go doesn't have pointer arithmetic.
The GC has been evolving from conservative to precise, in Go 1.1 it's gotten as far as "mostly precise".
"darwin/arm, android/arm: a contributor is working on these, some way to go."
- "Clean up and document the code, add unit tests. (Target Go 1.4)"
Oh sure, add features now and add the unit tests later. :-D #joking