notes-computer-programming-programmingLanguageDesign-prosAndCons-golang

tutorials

https://gobyexample.com/

http://tour.golang.org/#1

design notes

http://commandcenter.blogspot.co.il/2012/06/less-is-exponentially-more.html http://lambda-the-ultimate.org/node/4554 http://news.ycombinator.com/item?id=4158865

http://blog.golang.org/2010/07/gos-declaration-syntax.html

https://plus.google.com/116810148281701144465/posts/iqAiKAwP6Ce why Go uses errors instead of exceptions

notes on the previous

https://plus.google.com/116810148281701144465/posts/iqAiKAwP6Ce why Go uses errors instead of exceptions

" In Go, the established convention is that functions return errors; they don't panic. If a file isn't found, os.Open returns an error; it doesn't panic. If you write to a broken network connection, the net.Conn's Write method returns an error; it doesn't panic. These conditions are expected in those kinds of programs. The operation is likely to fail, and you knew that going in, because the API designer made it clear by including an error result.

On the other hand, there are some operations that are incredibly unlikely to fail, and the context is such that there is no way to signal the failure, no way to proceed. These are what panic is for. The canonical example is that if a program evaluates x[j] but j is out of range, the code panics. An unexpected panic like this is a serious bug in the program and by default kills it. Unfortunately, this makes it difficult to write robust, defensive servers that can, for example, cope with the occasional buggy HTTP request handler while keeping the rest of the server up and running. To address that, we introduced recover, which allows a goroutine to recover from a panic some number of call frames below. However, the cost of a panic is the loss of at least one call frame from the stack. We did this intentionally. To quote from the original mail: "This proposal differs from the usual model of exceptions as a control structure, but that is a deliberate decision. We don't want to encourage the conflation of errors and exceptions that occur in languages such as Java."

The post I mentioned at the start asks "why is an array out of bounds any more cause for panic than a bad format string or a broken connection?" The answer is that there is no in-band way to report the error during the evaluation of x[j], while there is an in-band way to report an error in a bad format string or a broken connection. (The format string decision is interesting by itself but orthogonal to this discussion.) "

" The error returns used by Go are admittedly inconvenient to callers, but they also make the possibility of the error explicit both in the program and in the type system. While simple programs might want to just print an error and exit in all cases, it is common for more sophisticated programs to react differently depending on where the error came from, in which case the try + catch approach is actually more verbose than explicit error results. It is true that your 10-line Python program is probably more verbose in Go. Go's primary target, however, is not 10-line programs. "

comments

" I frequently think back to a friend's comment on the Go compiler written by Ken Thompson: it's fast because it just doesn't do much, the code is very straightforward." -- http://neugierig.org/software/blog/2011/04/complexity.html

opinions

http://www.syntax-k.de/projekte/go-review

http://scienceblogs.com/goodmath/2009/11/11/googles-new-language-go/

http://www.acooke.org/cute/GoRocksHow0.html <-- good, read this one

http://blog.jgc.org/2012/07/things-i-like-about-programming-in-go.html

http://www.acooke.org/cute/GoRocksHow0.html

http://commandcenter.blogspot.com/2012/06/less-is-exponentially-more.html

http://news.ycombinator.com/item?id=4158865

http://lambda-the-ultimate.org/node/4629

http://www.syntax-k.de/projekte/go-review

http://blog.golang.org/2010/07/gos-declaration-syntax.html

http://scienceblogs.com/goodmath/2009/11/11/googles-new-language-go/

http://grenouille-bouillie.blogspot.com/2010/03/why-go-isnt-my-favourite-programming.html

http://monoc.mo.funpic.de/go-rant/

http://cowlark.com/2009-11-15-go/ go vs Brand X toread http://news.ycombinator.com/item?id=943554

http://news.ycombinator.com/item?id=1912728

" You pro­bab­ly heard about Go the Goog­le spon­sored, Rob Pike powered suc­ces­sor to C. I cer­tain­ly like Go’s philoso­phy es­pecial­ly the fact that Go wants to com­bine the ease of use and fun of dynamical­ly typed, in­terpreted lan­guages with the cor­rectnes and speed of statical­ly typed, com­piled lan­guages. Also, Go aims for minimal­ism. In contra­st to other lan­guages Go has very few keywords4.

I also like the fact that con­cur­ren­cy is built into the lan­guage in the form of CSP. In that re­gard Go is armed for the fu­ture just like Clojure. Go’s novel, “tolerant” type sys­tem with it’s ad-hoc in­ter­faces seems to by very con­venient, too.

Howev­er I rea­l­ly must say that Go’s syn­tax is some­how ugly. I don’t know why but it seems hard to read. Maybe it is be­cause I haven’t pro­gram­med in Go and if I star­ted I would ac­custom to it. But the first im­press­ion is not good.

Then there are some ques­tion­able de­sign de­cis­ions like no generics, de­fin­ing the visibil­ity of a variab­le/func­tion by writ­ing the first charact­er in lower­case or up­percase, no enums5 and no ex­cep­tions6. " -- http://eugenkiss.com/blog/2010/the-next-big-language/

"

    Seriously, how the heck is
     
      var p *[]int = new([]int) // allocates slice structure; *p == nil; rarely useful
      var v  []int = make([]int, 100) // the slice v now refers to a new array of 100 ints
     
    .. a better solution to “improving upon” C++ with a new language than, oh I don’t know ..
     
      int[] p = null; // declares an array variable; p is null; rarely useful
      var v = new int[100]; // the variable v now refers to a new array of 100 ints
     
    ..? I’m sure I’m missing something here, particularly since I don’t understand what a “slice” is, but I suspect I shouldn’t care. Oh, nevermind, I see now that it “is a three-item descriptor containing a pointer to the data (inside an array), the length, and the capacity; until those items are initialized, the slice is nil.” Great. More pointer gobbligook. C# offers richly defined System.Array and all this stuff is transparent to the coder who really doesn’t need to know that there are pointers, somewhere, associated with the reference to your array, isn’t that the way it all should be? Is it really necessary to have a completely different semantic (new() vs. make())? Ohh yeah. The frickin pointer vs. the reference. "

" I see Go has a fmt.Printf(), plus a fmt.Fprintf(), plus a fmt.Sprintf(), plus Print() plus Println(). I’m beginning to wonder if function overloading is missing in Go. I think it is; http://golang.org/search?q=overloading "

" Go can't grab and schedule C routines properly" (is this true?!? seems unlikely)

---

" I'm pretty sure that go's compile speed is significantly faster than java. It's hard to compare though because java compilation is not the same thing. There is the bytecode in the class files and on the fly compilation in the vm. As well as some stuff that happens when the bytecode is loaded and run.

A lot of go's compile speed comes from how simple it's syntax is besides the gains it gets from avoiding C++ dependency issues. Java's syntax while less complex than C++ is still complex compared to go's. And it won't have go's parse time savings. "

"

> Why did go "win" over erlang, scala, and clojure?

Erlang sucks at string handling which is a no-go in Web developing.

Scala, Clojure, people don't even bother to setup JVM. Python is popular in some degree because it's installed by default on Linux and OS X. "

---

http://stackoverflow.com/questions/9339560/erlang-versus-go-versus-rust-comparison

---

etfb 9 hours ago

link

I grew up with Pascal and its derivatives, so I have a strong sense of the aesthetic of programming languages. I can't take Go seriously, or bring myself to use it, because it appears to have no aesthetic sense at all. It's ugly! Strings of keywords and punctuation, no rhythm to it, just a lot of mess. Like a cross between C and Prolog, perhaps, with a smattering of Python but only the ugly bits. I really don't like it.

Now, if you want to see a recent language with a bit of style to it -- and bear in mind I know nothing of how it is to use in practice, so I'm basing my opinions entirely on the look of it -- I think Rust is one of the best of the pack. So much smoother.

TL;DR (for any Redditors who may have stumbled in on their way to the Ron Paul fansite): languages have a flavour, and Go's flavour is "mess".

reply

lobster_johnson 5 hours ago

link

Sure, Go is ugly. I had the same reaction as you. But it's so damn functional. There are plenty of warts, syntax and design-wise, but once you get over them, it's actually a decent language.

Compared to Erlang, for example, it's positively beautiful. I like Erlang's execution model, but the unnecessarily gnarly syntax and the antique approaches to some things (no real strings, no Unicode, tuples that depend on positional values instead of named attributes, horrible stack traces, tail-call recursion as a looping construct, etc.) has made me stay away.

Go is a decent alternative to both C/C++ and Ruby for me these days. There are plenty of things I don't like about Go, but it's easy to forgive many of them when the language makes you so productive (at least for the things I need it for; it's pretty bad at web app development and completely useless for GUI development), so I do. Sometimes you just want something that works, with minimal amounts of pain. C++ works, but is always painful to me because it feels like the language is working against me, not with me.

I love the simple syntax in Go; the no-nonsense approach to object-orientation, the way the typing system allows adding methods to arbitrary types, which may have been inspired by Haskell's typeclasses. I love having native Unicode strings, first-class functions, a simple package system, built-in concurrency and decent networking in the standard libs. I love the built-in documentation generator (although it's too simplistic at this point; why not Markdown?).

Things I don't like:

Right now, Go is, in my opinion, mostly an inelegant, weirdly-shaped thing that fits into the weird hole not filled by C and C++, a stepping stone to the next big language. I don't love the language, but I love how I can do the same things I was planning to use C++ or Ruby for; as a C++ replacement I gain productivity and simplicity; as a Ruby replacement I gain performance.

Go is, if anything, a very pragmatic compromise that favours simplicity over complexity at the expense of structural and syntactical elegance. It looks a lot like Java around 1.3-1.4, before the enterprise stuff derailed its focus. My concern is that Go has no particular philosophy as a foundation; it bakes in some current concerns (concurrency, networking, ease of use) and made itself a very useful hammer for hammering today's nails, but may not be as easy to extend to work with tomorrow's screws.

PS. I, too, come from Pascal — specifically, Borland's Object Pascal. While I have fond memories of that language, I'm not sure elegance was one of its main traits.

reply

grey-area 2 hours ago

link

Nice list of frustrations with Go, though I wouldn't call it inelegant - the syntax may be homely, but the simplicity gives it a certain minimalist elegance.

Re nils, yes this still feels pretty ad-hoc, I wonder if they'd do anything differently there now if they could? Probably there is no easy way out now they're past 1.0.

It would be nice to see an extensible range and I've had this thought myself (as have others), there has been some discussion on the list, and they're not sure: https://groups.google.com/forum/?fromgroups=#!topic/golang-n...

but then you can also turn it around and iterate by passing in a function to the structure to iterate:

    func (self *DataStructure) Iterate(fun IteratorFunc)

Re GOPATH, what do you mean here?

Re packages having one level - I actually love this - if you want to nest things in folders, Go is trying to tell you to split it into sub-packages (in that opinionated manner it sometimes adopts). Of course this might feel restrictive, but if a package has lots of files I'd rather it was split up and organised into packages with clear boundaries between them rather than into an interconnected set of files/folders which only makes sense to the author of the code because it's all in one package with no boundaries. This restriction means we will see apps composed of packages by default rather than all in one namespace because it's easier.

Re the versioning, they originally resisted versioning the language, but eventually gave up on that idea and used versions, so I suspect for packages they'll eventually recognise the need for this when the ecosystem of packages grows sufficiently. Versions do introduce problems of their own (dependency hell), but IMHO being able to reliably import snapshots of a package (tag, branch or version) will be essential long term without having to fork a repo and rename it just to do so. If go becomes popular you can expect this to be addressed by third parties if the go team doesn't bother.

Re the tagging, in a way I'm pleased that they avoid adding lots of features like this, as this is the stort of thing I hate even in the limited version they have (on struct fields) - it's untidy and gets misused and overridden to try to extend the language (see sql libraries in Go tagging fields with all sorts of their own meta-data).

Re commas on lists, this bothers me less than having to put semicolon & LF on every line, I think it's a fair trade.

Re philosophy, I can't speak for the Go authors, but I've found radical simplicity to be its overriding principle, which explains some of the limitations which annoy you above and the ditching of a lot of OO baggage which languages nowadays are expected to carry whether they want it or not. I've found the simplicity worth the trade for some small frustrations.

reply

... > Re GOPATH, what do you mean here?

I mean that the "go" suite of build tools (build, get, install) rely on a specific convention: Your GOPATH is supposed to point to folders each containing bin, pkg and src subfolders with all packages.

Let's say I am developing a package A that uses my own package B. Both on Github. You're supposed to then create: $GOPATH/src/A (which contains A's files as immediate children) and $GOPATH/src/B (ditto for B).

The manuals I believe suggest that you have something ~/projects/go, under which you create bin/src/pkg and put your work. But I don't organize my home folder like that and I find it presumptuous that the tools assume that I do, so I have ~/.go, which contains bin/src/pkg, and I symlink stuff into ~/.go/src. Acceptable if annoying.

> Re packages having one level - I actually love this

I want to use folders as an organizing unit, the way they are intended. It makes sense even for small packages.

Generic example (I have something very similar to this, but I don't want to go into details): I have a framework. There is a bunch of core interfaces, and then a bunch of implementations. Let's say that my interfaces are called Queue (with concrete implementations FooQueue?, BarQueue?, BazQueue? etc.), Block (again, a bunch of concrete impls) and Stream (same). For my brain not go insane every time I look at my source tree, I want to have the core interfaces in the root, and then subfolders queues/, blocks/, streams/.

That's not much to ask. Putting these things in separate packages with dependencies on the original package makes no sense, in particular because the original package has internal dependencies. For example, the framework has a configuration builder that connects queues, blocks and streams, and it knows certain things about the implementations and how they interconnect. Putting that into yet another package is just insanity.

It's one package. And it needs subfolders. (Especially with how Go mandates that tests be in the same folder as the unit being tested!)

I have encountered lots of C projects that pile all the files into a single root folder, instead of having a src/ folder with appropiate subfolders by category. I think that's what the Go guys are mimicking. I hate it.

I mean, just the fact that a package needs the Go files to be in the root is pretty bad. I may have a docs/ there, a readme file, an examples/ folder, a locales/ folder -- why must the source files go in the root? They're not that special. I know you can import directly from a src, but the convention is 'import "github.com/foo/bar"', not "github.com/foo/bar/src"'.

pjmlp 2 hours ago

link
 I also am a Pascal refugee, having started using it with Turbo Pascal 3.0, and most of the versions until Turbo Pascal for Windows 1.5 and a few toy projects with Delphi.

What attracted me to Go initially, was Oberon's influence in the language, like the packages, method signatures, Pascal declaration way and low level tricks exposed via the unsafe package.

But like yourself after a while using the language, before the 1.0 release, I came to the conclusion that I am better served with more expressive languages.

So nowadays I play around with C++11, Rust and D.

" But you can criticize its aesthetic nevertheless? Maybe if you could bring yourself to use it you might appreciate the elegance of the Communicating Sequential Processes implementation. Or maybe the clean OOP practices derived from using interfaces. Or the beauty of coroutines mapped unto multiple OS threads. "


davidw 11 hours ago

link

I'm interested in tracking Go as a replacement for Erlang. Some things that should probably happen:

I think they'll get there eventually. Maybe not 100%, but 'good enough' sooner or later.

reply

jerf 7 hours ago

link

Right now, as near as I can tell, you basically can't implement an Erlang supervision tree in Go. You don't have anything like linking, which really has to be guaranteed by the runtime to work properly at scale, so bodging something together with some 'defer' really doesn't cut it. You also can't talk about "a goroutine", because you can't get a reference or a handle to one (no such thing), you can only get channels, but they aren't guaranteed to map in any particular way to goroutines.

I've done Erlang for years and Go for weeks, so I'm trying to withhold judgement, but I still feel like Erlang got it more right here; it's way easier to restrict yourself to a subset of Erlang that looks like a channel if that is desirable for some reason than to implement process features with channels in Go.

reply

JulianMorrison? 8 hours ago

link

That issue is quite simply a misinterpretation of goroutines.

Erlang: perform a maximum number of reductions, then switch, or switch on IO. The number of reductions is cleverly adjusted so that a process which is swamping other processes will be throttled.

Go: switch on IO.

Go's design is much simpler, and closer to Ruby Fibers than to Erlang processes, except that goroutine scheduling can use multiple threads. To cooperatively switch without doing IO, call runtime.Gosched().

reply

trailfox 10 hours ago

link

Akka is also a viable Erlang alternative.

reply

waffle_ss 6 hours ago

link

Looks like a nice library but I don't think it's a serious contender to replace Erlang because the JVM just isn't made for the level of concurrency that Erlang's VM is. Off the top of my head as an Erlang newbie:

[1]: http://doc.akka.io/docs/akka/snapshot/general/jmm.html

reply

---

GhotiFish? 6 hours ago

link

Whenever I look through golangs specs, I always get stuck on the same question.

Why are the methods we define restricted to the types we define? I'm SURE there is a good reason.

Others have said that it's because if you did allow that kind of thing to happen, you might get naming collisions in packages. I don't buy this argument, you could get naming collisions anyway from packages, Go resolves those issues by aliasing. Go also allows for polymorphic behavior by packaging the actual type of the method caller with its actual value, so resolving which method to use isn't any more complicated.

I don't get it, I'm sure there's a good reason! I just hope it's a good enough reason to throw out the kind of freedom that would allow you.

reply

Jabbles 6 hours ago

link

You can't add methods to a type in a different package as you might break that package. By locking the method set of types when a package is compiled you provide some guarantee of what it does, and it doesn't need to get recompiled again! This is central to Go's ideals of compiling fast.

Besides, embedding a type is very easy http://golang.org/ref/spec#Struct_types

reply

GhotiFish? 6 hours ago

link

Hmmm. I can see how adding methods to a type in a different package would require that package to be recompiled, but I don't see how I could break that package. Unless there's some reflection magic I'm not considering.

I'm reading through the embedded types now. I am new to golang so this one is lost on me. I thought if you wanted your own methods on a type from another package, you just aliased it with your own type def.

though it looks like there's some kinda prototyping behavior being described here?

    If S contains an anonymous field T, the method sets of S and *S both include
    promoted methods with receiver T. The method set of *S also includes promoted 
    methods with receiver *T.
    
    If S contains an anonymous field *T, the method sets of S and *S both include 
    promoted methods with receiver T or *T.

reply

Jabbles 5 hours ago

link

For example, if you do a runtime type assertion to see if a variable satisfies an interface:

    v, ok := x.(T)

http://golang.org/ref/spec#Type_assertions

If you "monkey-patch" x to satisfy T in another package, the value of ok may change.

reply

GhotiFish? 5 hours ago

link

Hmm I can kind of see what you mean, but I don't see it as a big of a problem.

If you make package A depend on package B, package B monkey-patches x with method Foo so now x is a Fooer

x now satisfies the Fooer interface in package A, well that seems ok. You imported B after all. In things that don't import B, x doesn't satisfy Fooer. Is this unexpected behavior? If B depends on C, C's x won't satisfy Fooer right?

reply

enneff 4 hours ago

link

It's because Go was designed for large code bases, and doing what you describe leads to unmaintainable code.

You might be interested in this document: http://talks.golang.org/2012/splash.article

reply

pcwalton 5 hours ago

link

Because if you had package A that defined type T, package B that defined method Foo on T and, separately, package C that defined method Foo on T, then B and C could not be linked together.

reply

GhotiFish? 5 hours ago

link

I don't see why, personally, B defines Foo on T, so whenever B uses Foo, it should use B's Foo. C defines Foo on T, so whenever C uses Foo, it should use C's Foo. why is it ambiguous for the compiler which one to use?

reply

pcwalton 5 hours ago

link

If package D linked against B and C and called Foo(), which Foo() would it call?

reply

GhotiFish? 5 hours ago

link

Good question. If package B defined the function Bar(), and package C defined the function Bar(), then if package D linked to packages B and C, which function should it call when it asks for Bar()?

Naming collisions are a solved problem.

reply

pcwalton 5 hours ago

link

You don't have to import methods in Go; the compiler just finds them. They're in a per-type namespace and therefore would be vulnerable to collisions. Of course, they could change the language to allow/require you to import methods, but that would add some complexity.

On the other hand, you have to import functions, so your example isn't a problem.

reply

mseepgood 4 hours ago

link

In Go each external function call is preceded by its package name to avoid collisions, but you don't have packages within the method namespace of a type.

reply


go needs generics discussion: https://news.ycombinator.com/item?id=5442339

---

dchest 10 hours ago

link

It doesn't matter much in Go: integer types with defined sizes are intXX/uintXX, which are not convertible to int/uint implicitly.

http://golang.org/ref/spec#Numeric_types

reply

---


An Erlang process has its own heap, so when it blows up, its state just goes away, leaving your remaining program's state untouched. With Go, there is no way to recover sanely; even if your goroutines are designed to copy memory, Go itself has a single heap.

Now, this is a very odd design decision for a language that claims it's designed for reliability. Perhaps Go's authors thinks it's better just for the entire program to die if a single goroutine falls over; well, that's one way, but it's a crude one. Erlang's design is simply better.

I wonder if Go can ever adopt per-goroutine heaps, or whether it's too late at this stage. I was happy to see that Rust has chosen to follow Erlang's design by having per-task heaps, even if all the pointer mechanics (three times of pointers, ownership transfer, reference lifecycles and so forth) result in some fairly intrusive and gnarly syntax.

reply

masklinn 1 day ago

link

> Go allows you to share memory between goroutines (i.e. concurrent code).

Go will share memory, by default, and special attention must be taken preventing or avoiding it. It's not an allowance.

> In fact, the Go team explicitly tells you not to do that

And yet they have refused to implement a correct model, even though they have no problem imposing their view when it fits them (and having the interpreter get special status in breaking them, see generics).

reply

burntsushi 1 day ago

link

> Go will share memory, by default, and special attention must be taken preventing or avoiding it.

Not really. If you use channels to communicate between goroutines, then the concurrency model is that of sequential processes, even if channels are implemented using shared memory under the hood.

That is, the default concurrency model militated by Go is not shared memory, but that of CSP. It's disingenuous to affix Go with the same kind of concurrency model used in C.

> And yet they have refused to implement a correct model

What is a correct model? Erlang's model isn't correct. It's just more safe.

> (and having the interpreter get special status in breaking them, see generics)

What's your point? Purity for purity's sake?

reply

masklinn 19 hours ago

link

> Not really. If you use channels to communicate between goroutines, then the concurrency model is that of sequential processes

Except since Go has support for neither immutable structures not unique pointers, the objects passed through the channel can be mutable and keep being used by the sender. Go will not help you avoid this.

> That is, the default concurrency model militated by Go is not shared memory, but that of CSP. It's disingenuous to affix Go with the same kind of concurrency model used in C.

It's not, go passes mutable objects over its channel and all routines share memory, you get the exact same model by using queues in C.

fulafel 18 hours ago

link

Channels are pass-by-value. If you pass a struct, it's copied, and both sides can mutate their own copies as much as they want.

You can get still bugs if you make channels of pointers (or have pointers in your message structs etc).

reply

masklinn 15 hours ago

link

> You can get still bugs if you make channels of pointers (or have pointers in your message structs etc).

Confirming that

> the objects passed through the channel can be mutable and keep being used by the sender. Go will not help you avoid this.


http://research.swtch.com/generic


    A technical point: you can achieve destructor-style resource management in Go like so:
    ------
    interface Disposable { Dispose() }
    func using(r Disposable, f func()) {
    defer r.Dispose();
    f();
    }
    // example
    conn := db.NewConnection(connString);
    using(conn, func() {
    ... conn ...
    });
    --------
    Thanks to the interface, this could be done generically as well. It also illustrates another powerful feature of Go, anonymous closures, that C++ is only just getting.
    Rivorus (December 12, 2009 6:07 PM) "I've little doubt that you could find a way to parse XML into C++ structs using template metaprogramming, but I don't think it would be pretty, and of course the I/O time would dominate anyway, so it wouldn't be measurably more efficient.
    Very true, however with the Boost.Spirit library in C++ you can do it very nicely and it's surprisingly intuitive as well. Of course, you don't want to look at all of the library code that makes it possible unless you are really into metaprogramming as you have pointed out.
    "A technical point: you can achieve destructor-style resource management in Go like so:"
    That's actually exactly what I am talking about. What you posting is exactly the type of thing that destructors accomplish implicitly and with less code. As well, with just defer the "incorrect" solution happens by default if you forget. The code you posted is analogous to the C# or Java code mentioned in the post by Herb Sutter that I linked to. Everything he states when talking about Java and C# there applies to Go with respect to defer, minus the part about exceptions since Go doesn't [currently] have exceptions.
    Jonathan Amsterdam (December 13, 2009 8:57 AM) First, let's keep hold of the thread of the argument. Your claim was that defer interfered with generic programming. I demonstrated that that was false, assuming everyone adopted a certain convention. (You could now argue that Go has done nothing to foster such a convention, and I would agree with you.)
    Now let's talk about destructors vs. using for resource management:
    Some minor points about Sutter's post: He writes the Java version of Transfer incorrectly. In a correct version, the commenter's point about exceptions thrown by Dispose would not apply. (You'd nest the try blocks.) And I don't know why he thinks you'd have to wait for a GC to release the queues' resources. Dispose() would do it immediately.
    But I agree with Sutter's main point: destructors are better when applicable. Most of my day-to-day programming is in C++, and I love this aspect of the language. But we should consider how often they are applicable. Definitely for file I/O, since the most common pattern is to read or write completely. Not at all for locks, because locks are always longer-lived than the scope in which you lock them; so in C++ you need a wrapper class for RAII, and the code is about the same as you'd get with "using". Sometimes for network connections; but often you want to hold on to a connection for a while before tearing it down, and you may want to free it asynchronously, say after it has been idle for a while. You end up with some sort of connection pool, and then I don't think the code looks very different in C++ vs. C#.
    It's worth pointing out that garbage-collected languages have the disadvantage that there is no delete, and typical garbage collectors may run at some arbitrarily distant point in time after a resource is released. I think Limbo had a great solution to that: it used reference counting for non-cyclic structures, and guaranteed that they would be freed immediately upon becoming unreferenced. As a practical matter, resource-holding objects are almost never cyclic, so the idea should work well (though I have no practical experience with it).
    As for Go, I don't see it adopting destructors, because that would lead down the path of constructors, assignment operators and the like, and the Go designers are very clear that they don't want to go down that route. It's plausible that Go will adopt GC finalizers, but beyond that I think we should not expect any help from the language.
    Julian Morrison (December 13, 2009 11:37 AM) I really don't see the advantage in immediate deallocation of memory. "free" isn't free. You are shuffling memory in itty bitty slices, when a compacting GC could clear and release it in bulk.
    If you need to access a resource which needs cleanup through an interface, perhaps you should have a "Release()" method as part of the interface, which you can defer.

Jonathan, I think you miss the point about the limits of "defer". In R.'s ABCD example, if something in C calls for defer code, there's no way for the person coding A to know that, so it can't be written. A destructor is the only place where it could be put.

Rivorus (December 13, 2009 12:40 PM) [2-part again]

"Your claim was that defer interfered with generic programming. I demonstrated that that was false, assuming everyone adopted a certain convention."

You did nothing of the sort. You showed exactly what I was saying, you have to jump through hoops and be explicit every single time you create an object with nontrivial disposal when in actuality you should get the correct behavior implicitly by default and with no chance of error. The language is Turing complete, no one is saying that you can't reach the same end goal for a given application both with and without destrutors, only that it is needlessly more difficult without them and more error prone.

"I don't know why he thinks you'd have to wait for a GC to release the queues' resources. Dispose() would do it immediately."

He didn't say that Dispose wouldn't do it, he said that if you as the programmer do not explicitly Dispose then you get resources released at a noneterministic time after the object leaves scope (finalization). This simply cannot happen when you use destructors since the correct behavior happens automatically.

"He writes the Java version of Transfer incorrectly. In a correct version, the commenter's point about exceptions thrown by Dispose would not apply. (You'd nest the try blocks.)"

He is writing the simplest code whose end result is comparable to the corresponding C++/CLI code that he posted to illustrate his point about having to be explicit. He could have nested try blocks but it would have just made the code even more complicated than it already is. If you want to really be pedantic, you are also running into the fundamental differences between how exceptions are dealt with in C++ and in Java -- in C++, exceptions thrown but never caught in the destructor do not propagate, they cause a terminate, meaning that if you really wanted to make the code as closely comparable to C++ as possible, not only would you need all those try/cathes, but you'd be terminating rather than propagating if a dispose let an exception leak through. All of this is beyond the point of his post but only further argues in favor of the approach with destructors. Again, destructors are automatically called whereas "defer" or the dispose pattern of Java and C# require you to be explicit every time a type with non-trivial disposal is instantiated.

"But we should consider how often they are applicable. Definitely for file I/O, since the most common pattern is to read or write completely. Not at all for locks, because locks are always longer-lived than the scope in which you lock them; so in C++ you need a wrapper class for RAII, and the code is about the same as you'd get with "using". Sometimes for network connections; but often you want to hold on to a connection for a while before tearing it down, and you may want to free it asynchronously, say after it has been idle for a while. You end up with some sort of connection pool, and then I don't think the code looks very different in C++ vs. C#. "

All of those cases are perfect examples of where destructors are applicable, and in fact I use RAII in those situations all of the time and would certainly not trade it for "defer". In the hypothetical cases you are talking about, for instance where locks are unlocked after the locking object goes out of scope (which I don't know why you claim is always the case, it's actually almost never the case), you are exhibiting times where you either declare the lock object at one scope and lock later on, or, less likely, you want shared ownership of the lock object. With C++ you are able to very easily and automatically handle both cases and still get deterministic unlocking immediately after all of the objects end their lifetime with virtually no chance of error. Further, I'm not sure what you are even trying to point out here since you are providing no example of a better approach with "defer".

...

Here is a better example since you say:

"... who knows what the next library version will bring"

The situation with destructors is both automatic and much safer with respect to future development. Imagine that type "A" has trivial disposal so the programmer decides to have no "defer" statement when it is instantiated. Later on in development -- weeks, months, or years -- the type "A" grows and implementation details change to where it now should close down resources. All of a sudden there should be a deferred disposal when you instantiate "A".

So what happens to that previous code that was written? Since "defer" wasn't used, you now have to go back and insert it everywhere, otherwise nothing is properly cleaned up.

The way around this horrible scenario would be to have made the original type "A" have an empty "dispose" method and the places that instantiate it have a "defer" statement that calls the empty dispose, postulating that sometime in the future "A" may evolve to have nontrivial disposal. If you abide by this pattern you now you have an explicit "defer" everywhere you instantiate a scope-bound instance of your type even though its dispose currently does nothing.

What then is the benefit of that over having it automatic? If every time you instantiate your type you should be defering disposal, you lose absolutely nothing by combining the operations implicitly. You have now made the type impossible to misuse.

This does not even cover places with shared ownership, where disposal happens deterministically after it has no more owners. You still have to do extra management -- management which is done automatically in C++ because of RAII with respect to smart pointers. Doing this with only a feature like "defer" is far from feasible.

Carsten Milkau (May 27, 2011 5:35 AM) You forgot

4. (The python way) Make types first-class objects. That means, type factories are full-fledged ordinary functions instead of lexical patterns.

It's certainly not fair to compare an interpreted, weakly-typed language to a compiled, strongly-typed one. Still, Go has already gone quite a bit into this direction with the Reflect package.

To put it straight, generics are actually a specialized means for computation on types. There are many cases where they are easy and straightforward, but Python shows that first-class types can do it just as well, and the rapid evolution of the boost libraries shows that there is actually a great demand in general type computation (and also that generics are inferior for this purpose). Compilation is a computation, and it only makes sense to use the same language for compile-time computation as for runtime computation doesn't it?

I don't say this would be easy, not at all. Yet I think following this path would make Go even more useful, productive and original.

---


in Go and Haskell, you can define methods on unboxed objects, e.g. ints, whereas in Python you can only define methods on objects which are essentially boxed via a pointer

in Go, you don't need an 'implements' declaration

---

http://thornydev.blogspot.com/2013/01/go-concurrency-constructs-in-clojure.html

---

" The Uniform Access Principle (UAP) was articulated by Bertrand Meyer in defining the Eiffel programming language. This, from the Wikipedia Article pretty much sums it up: “All services offered by a module should be available through a uniform notation, which does not betray whether they are implemented through storage or through computation.”

Or, alternatively, from this article by Meyer: “It doesn’t matter whether that query is an attribute (representing an object field) or a function (representing a computation); this is an internal representation decision, irrelevant to clients accessing objects through calls such as [ATTRIB_ACCESS]. This “Principle of Uniform Access” — it doesn’t matter to clients whether a query is implemented as an attribute or a function” "

---

" Lack of Struct Immutability

By “immutability” I mean that there’s no way for a framework to prevent changes to structures that are supposed to be managed by the framework. This is related to Go’s non-conformity to the UAP (above). I don’t believe there’s a way to detect changes either.

This can probably be done by implementing immutable/persistent data structures along the line of what Clojure did, but these will not have literal forms like Arrays and Maps do now in Go. Once again, this is, in part, an issue of non-idiomatic use of Go. "

---

" Unyielding Enforcement of ‘Unused’ Errors

The implementors of Go have made a couple of rules that I’m very happy that they’re enforcing. Two in particular: “there are no compiler warnings, only errors”, and “unused things (variables, imports, etc.) are errors”.

The annoying part is that a Go programmer can’t get around this even temporarily. So if there’s something they are trying to experiment with (i.e. figure out how it works) it isn’t always possible to just comment out surrounding code. Sometimes the commenting makes variables or import statements unused… and it won’t compile. So the programmer has to comment out still more code. This can happen a few times before it successfully compiles. Then the programmer has to uncomment the stuff just commented out. Error prone, messy, etc.

There are hacky ways to get around this, but these allow unused things to end up in the final code if programmers are careless/forgetful/busy/rushed.

This is really just an annoyance but it stands out, to me at least. Here’s a compiler that comes with tools for sophisticated functions like formatting, benchmarking, memory and CPU profiling, and even “go fix” yet they leave us temporarily commenting out code just to get it to compile? Sigh. "

--

http://lambda-the-ultimate.org/node/3896#comment-58378

Tom Lord thinks "panic" should not be able to pass a value which becomes the return value of 'recover'

(it sounds to me like i don't like 'panic' at all..)

--

"

My whole attitude changed when Michael Piatek (one of the star engineers in the group) sent me an initial cut at the core system rewrite in Go, the result of less than a week's work. Unlike the original C++ based system, I could actually read the code, even though I didn't know Go (yet). The #1 benefit we get from Go is the lightweight concurrency provided by goroutines. Instead of a messy chain of dozens of asynchronous callbacks spread over tens of source files, the core logic of the system fits in a couple hundred lines of code, all in the same file. You just read it from top to bottom, and it makes sense.

Michael also made the observation that Go is a language designed for writing Web-based services. Its standard libraries provide all of the machinery you need for serving HTTP, processing URLs, dealing with sockets, doing crypto, processing dates and timestamps, doing compression. Unlike, say, Python, Go is a compiled language and therefore very fast. Go's modular design makes for beautiful decomposition of code across modules, with clear explicit dependencies between them. Its incremental compilation approach makes builds lightning fast. Automatic memory management means you never have to worry about freeing memory (although the usual caveats with a GC-based language apply).

Being terse

Syntactically, Go is very succinct. Indeed, the Go style guidelines encourage you to write code as tersely as possible. At first this drove me up the wall, since I was used to using long descriptive variable names and spreading expressions over as many lines as possible. But now I appreciate the terse coding approach, as it makes reading and understanding the code later much, much easier.

Personally, I really like coding in Go. I can get to the point without having to write a bunch of boilerplate just to make the compiler happy. Unlike C++, I don't have to split the logic of my code across header files and .cc files. Unlike Java, you don't have to write anything that the compiler can infer, including the types of variables. Go feels a lot like coding in a lean scripting language, like Python, but you get type safety for free.

..

Learning Go is easy coming from a C-like language background. There are no real surprises in the language; it pretty much makes sense. The standard libraries are very well documented, and there are plenty of online tutorials. None of the engineers on the team have taken very long at all to come up to speed in the language; heck, even one of our interns picked it up in a couple of days.

Overall, the rewrite has taken about 5 months and is already running in production. We have also implemented 3 or 4 major new features that would have taken much longer to implement in the original C++ based system, for the reasons described above. I estimate that our team's productivity has been improved by at least a factor of ten by moving to the new codebase, and by using Go.

...

Why not Go?

There are a few things about Go that I'm not super happy about, and that tend to bite me from time to time.

First, you need to "know" whether the variable you are dealing with is an interface or a struct. Structs can implement interfaces, of course, so in general you tend to treat these as the same thing. But when you're dealing with a struct, you might be passing by reference, in which the type is *myStruct, or you might be passing by value, in which the type is just myStruct. If, on the other hand, the thing you're dealing with is "just" an interface, you never have a pointer to it -- an interface is a pointer in some sense. It can get confusing when you're looking at code that is passing things around without the * to remember that it might actually "be a pointer" if it's an interface rather than a struct.

Go's type inference makes for lean code, but requires you to dig a little to figure out what the type of a given variable is if it's not explicit. So given code like:

    foo, bar := someFunc(baz) 

You'd really like to know what foo and bar actually are, in case you want to add some new code to operate on them. If I could get out of the 1970s and use an editor other than vi, maybe I would get some help from an IDE in this regard, but I staunchly refuse to edit code with any tool that requires using a mouse.

Finally, Go's liberal use of interfaces allows a struct to implement an interface "by accident". You never have to explicitly declare that a given struct implements a particular interface, although it's good coding style to mention this in the comments. The problem with this is that it can be difficult to tell when you are reading a given segment of code whether the developer intended for their struct to implement the interface that they appear to be projecting onto it. Also, if you want to refactor an interface, you have to go find all of its (undeclared) implementations more or less by hand. "

http://talks.golang.org/2012/splash.article

--

"

AnonymousAugust? 20, 2013 at 1:13 AM

No scheduler preemption in Go compared to Erlang though :(

The lack of per-process heaps and garbage collection is also scaring me away from Go. "

"

" It's quite a different language, much more so than Java compared to C# (1.0 even).

A couple of things of the top of my head:

" I was honestly hoping Go would give us head-to-head battle with C# but from initial looks Go pretty much feels like C# 1.0. No generics, no partial classes, no linq... C# 5.0 even has async and dynamic types. I like minimalism but these features are something we take for granted in languages that were created post-2000 era. "

--

--

continuations 2 days ago

link

The author did say CPU efficiency is key, and Erlang isn't exactly known for its raw speed.

In general search is very CPU intensive, maybe that's why Google never adopted Erlang.

reply

banachtarski 21 hours ago

link

Yes but intentionally so. There is an intrinsic tension between latency and throughput. Erlang chooses willfully to optimize for the former rather than the latter. This works when the majority of the tasks occurring concurrently are typically small and lightweight (aka, a web server).

reply

--

--

" Some requirements in that set (e.g. serving data on dl.google.com) could be solved perfectly well by Erlang, and could have been for years, but can also now be solved perfectly well by Go. That they are now being solved by Go is likely an effect of the "Golang advocacy group" that has formed at Google.

Others can't be solved by Erlang, but can be solved by Go (e.g. CPU-bound matrix-multiplications for PageRank? index calculation), or vice-versa (e.g. deploying new Google Talk daemon code without dropping the XMPP connection.) "

--

" Go clearly has a pretty different set of design priorities, not the least of which are static typing and native code compilation."

-- "

Go's concurrency was not "inspired by" Erlang. It was inspired by Hoare's CSP. Pike and Luca Cardelli created a CSP based language called Squeak in 1985: http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.26.8... "

--

jlgreco 2 days ago

link

Go basically does have exceptions, though they call it something else, it is slightly less powerful than you would normally expect, and the idiomatic usage is very different.

Honestly I that Go's idiomatic usage of panic/recover is a good idea even in other languages. Exceptions passing package boundaries is typically a pain in the ass in practice, it makes for ugly code.

(My "day job" is typically Java these days, some Scala. I am not speaking from inexperience like many Go detractors like to assume. There seems to be a perverse notion that anyone who dislikes exceptions must not understand them. Silly.)

--

" Go After 2 Years in Production After running Go for two years in production at Iron.io, I wanted to share our experience/feelings about it. We were one of the first companies to use Go (golang) in production and we didn't know what to expect in the long run, but so far, so great.

... specific things that we love about the language, the things we learned along the way. In no specific order, here they are:

    Performance
    Memory
    Concurrency
    Reliability
    Deployment
    Talent

... Performance ...

"

go vs java, just a little slower (mb 1x); go vs ruby faster (mb 10x)

" ... Memory ... Concurrency

Concurrency is a huge part of Go with some high level constructs that make it a breeze to use. I used Java for many, many years and got very comfortable with the java.util.concurrency package, which is a pretty nice set of concurrency tools, but it's just not the same as Go in terms of simplicity or the underlying implementations. Go has goroutines for concurrent operations and channels for communicating between them. Goroutines are particularly interesting: ... segmented stacks. ... One thing we had to do here was limit concurrency to make sure we didn't overload our database or other services during spikes. We did this with a simple semaphore using Go channels. ...

Reliability

Reliability in a language is kind hard to quantify, but we've found our Go applications to be very robust. I don't think we've had a failure/crash that wasn't related to some external problem (read: database or some bad library). In general though, there are very high quality open source libraries out there for Go. We've found no memory leaks and no major core library bugs.

...

Deployment

Go compiles into a single, static binary file so deployment is simply putting the file on a server and starting it up. No dependencies required. No runtime required (you don't need to install Go on the server). And it's small; the IronMQ? binary is ~6MB.

Rolling Back

If something goes wrong after deploying and you need to roll back, you can just stop the bad process then start the previous binary. You don't need to worry about a dependency being upgraded since the entire program is compiled into a single binary.

Talent

... "

upvote

neya 10 hours ago

link

Another Scala user here. And I've been using Scala for the last six months. Mostly developing web applications on Scalatra and Play. I love Scala - It still has its goodiness and it's an excellent alternative to Java.

If you notice in all of the Scala books (I've read the one by Martin and the One by David Pollak as well), they all seem to push you towards the functional programming model instead of the much more common imperative model.

But you know, there's a problem with that. Not with the language itself, but with the web development side of Scala. There's only certain ways by which you can program with a particular function that accepts a request and returns a response. So, most of the time, I would find myself wasting time thinking "How can I make this more functional?"

Functional programming is really great when you can actually enforce it - When writing algorithms and so forth, but otherwise, you are forced to fallback to this imperative model which sucks, because you have to constantly keep changing your mindset between these two models.

Another downside of Scala is its HUGE syntax. Processing strings? Here's a ten million ways to do it.. (a bit over-exaggerating). Oh and if you chose this way, then you should use this syntax. No, not that one, THIS one. Oh no, not that one, this variation of that one that looks like this.

I've advocated Scala to many HN members here in the past, you can even check out my comments, for instance. But from my experience, I think Scala is an Academic language. But it's superior in its own ways - I just LOVE the concept of avoiding nulls and passing an Option. It's beautiful. But the downside is its huge academic complex syntax. I want to be able to write code which shouldn't reduce my hair count even if I look at it after 6 long months. I don't want to refer to an 800 page book each time I'm confused about something.

That's why I think Go is beautiful. The syntax is just enough to keep it inside your head forever. I fear that as the language matures, this might also evolve into something as complex as Scala, but let's hope not so.

Go isn't a magic bullet though. It has its own downsides, but nothing w.r.t performance or similar. For the most part, it's awesome.

Once you go Go, you never go back.

P.s - I still love Scala as well ;)

reply

upvote

rybosome 7 hours ago

link

I've historically been pretty pro-Scala 'round these parts, so my response to this should be considered in that light. So, with that disclosure out of the way... =)

I have to admit, I don't agree with the assertion that the cognitive dissonance associated with programming in an imperative way versus a functional way is a major problem to Scala. I realize personal preference is a very big part of this, but there are many problems which are just easier to solve in a functional way versus an imperative way. Of course, the opposite holds true as well. This is the beauty of Scala; one can write performance-critical imperative code while exposing a functional interface; or, one can consume a fluent functional interface to produce imperative code.

Frankly, I guess I've become something of a functional zealot, so my problem with Go is that it's so stubbornly imperative. This is why I can't get behind Go, as much as I want to. I feel like it doesn't consider many of the lessons that have been learned about FP making life easier. Set operations (map, filter, reduce) are insanely common, yet doing them imperatively sucks to the point of discouraging them. Excessive mutability is difficult to reason about. Nobody expects the nil/null/none inquisition. We tend to forget about side effects. Without an extensible language, which requires truly first-class functions, you're at the language designer's mercy.

Hell, Scala probably isn't the be-all, end-all answer to all of this. I just don't think that a doggedly-imperative, non-composable, statement-oriented language is the future of programming "in the large", not when the past has shown us that we tend to produce buggy, unmaintainable software this way. I'm pragmatic enough to realize that pure FP isn't a solution, but I feel strongly that sticking to traditional imperative because it's familiar is a costly mistake.

I can't argue with your take on the syntax, though, since that's personal preference. =) If you have any thoughts on why you feel productive in Go, I'd love to hear them; as I've said, I've been really struggling with motivating myself to learn and enjoy it.

reply

" And back to the OP, Go is a platform that brings JVM-class speed (the capability to return a login page in ~3ms) to those who can't stomach Java. Bravo to Go!

"

"

upvote

paddyforan 9 hours ago

link

I think one of my favourite things about Go is that it makes it easy and obvious to code well. Generally the first thing that comes to my mind is going to be performant and extendable.

The reason this is the case, I think, is that Go strives to make algorithmic complexity clear: you know when you're allocating memory, but you don't need to jump through hoops to do it. You know the rough performance costs of what you're doing because the core team works hard to make it obvious. For example, some regular expressions features (lookahead, if I remember correctly) aren't implemented in the Go standard library's regular expression package, because they're impossible to achieve in O(n) time. https://groups.google.com/forum/#!topic/golang-nuts/7qgSDWPI...

This level of care in making simple, clearly-defined tools with known properties makes it easy to code well. Ruby, Python, PHP, NodeJS?... you can shoot yourself in the foot and not even notice.

reply

--

upvote

Pxtl 10 hours ago

link

I know it's cliche, but I still can't live without generics or a macro-like approximation thereof. I suffered under Java 2 for too long to go back to that.

upvote

rprospero 10 hours ago

link

It's not cliche, it's just personal. For instance, most of the code I write is numerical code. To that end, any language without operator overloading is a non-starter, since it's far harder to find a bug in:

div(add(mult(-1,b),sqrt(sub(pow(b,2),mult(mult(3,a),c)))),mult(2,a))

than it is in

(-b+sqrt(b^2-3ac))/(2*a)

On the other hand, if I wrote server code, operator overloading would be far less useful. I'd probably curse any programmer who used it and thank the gods that it was left out of Go.

Conversely, since I write a lot of numerical code, I don't care about generics or typing, which is crucial to many other programmers. Generics don't matter since everything I work with is either a Double or a collection of Doubles. Similarly, static typing doesn't help, since most functions just have the type (Double -> Double) and the type checker can't tell a sine from a logarthim. Of course, the reverse is also true. Since everything is either a double or a collection of doubles, the fancy tricks that dynamic languages offer don't give me a damn thing, so I'm extremely ambivalent about the typing debate.

Of course, on other projects, I've written code that benefited from static typing and I've written valid code that would choke any type checker. I've written code that heavily needed generics. When I did that, I used the languages that had the features I needed.

Go just won't work for the programs I write and it sounds like it doesn't work for yours, either. That's why we won't use Go. I've heard it works wonderfully for a certain class of server software and I'm glad those guys have a useful language for their domain. If I ever have to write another server, I might even teach myself Go. But don't feel guilty that you're using an old hammer instead of the shiny new saw.

--

" I for one have found Go great for computing. The really quick compile times with static checking plus the composability are great. It definitely depends though; while the native concurrency is great there aren't a lot of easy solutions for non-shared memory computations. (I saw an MPI package at one point, but I haven't tried to use it) "

upvote

binarycrusader 9 hours ago

link

(disclaimer: My opinion on Go is based on my own limited experience from writing a few private projects using OpenGL?.)

Go seems like an exercise in frustration to me at the moment for anything GUI or low-level OS-related.

Many GUI libraries use an object model that's difficult to map onto Go's heavily restricted interfaces, especially with a lack of generics.

In addition, interacting with popular libraries (such as libsdl or even OpenGL?) that use thread-local variables (TLS) means using ugly workarounds like this one:

http://code.google.com/p/go-wiki/wiki/LockOSThread

So I think it really comes back to the "right tool for the right job." For most command-line utilities, and for anything networking-related, Go would be my first choice.

But for anything that needs a modern GUI toolkit and uses OpenGL?, it would be difficult for me to justify.

Again, I love the model Go provides for programs and packages purely written in Go; it's only when interfacing with system-level components that I get cranky.

--

 "I enjoyed Prolog when I was in university, so I might well enjoy Erlang also, but part of what I find attractive about Golang is the fact that it compiles to machine code and does not have any dependencies, making deployment easier. I also find myself agreeing with many of the design decisions: The lack of Exceptions -- a horrible source of complexity in C++; The Object model favouring composition over inheritance; the coherent toolset; I even think the braces decision is a good thing. Personally, I don't much care what the standard is, as long as there *is* a standard. Having tools to enforce a standard, any standard, is a wonderful bonus. Furthermore, I think your ad-hominem disparagement is unwarranted, and totally beside the point.

--

" a list of significant simplifications in Go over C and C++:

    regular syntax (don't need a symbol table to parse)
    garbage collection (only)
    no header files
    explicit dependencies
    no circular dependencies
    constants are just numbers
    int and int32 are distinct types
    letter case sets visibility
    methods for any type (no classes)
    no subtype inheritance (no subclasses)
    package-level initialization and well-defined order of initialization
    files compiled together in a package
    package-level globals presented in any order
    no arithmetic conversions (constants help)
    interfaces are implicit (no "implements" declaration)
    embedding (no promotion to superclass)
    methods are declared as functions (no special location)
    methods are just functions
    interfaces are just methods (no data)
    methods match by name only (not by type)
    no constructors or destructors
    postincrement and postdecrement are statements, not expressions
    no preincrement or predecrement
    assignment is not an expression
    evaluation order defined in assignment, function call (no "sequence point")
    no pointer arithmetic
    memory is always zeroed
    legal to take address of local variable
    no "this" in methods
    segmented stacks
    no const or other type annotations
    no templates
    no exceptions
    builtin string, slice, map
    array bounds checking

And yet, with that long list of simplifications and missing pieces, Go is, I believe, more expressive than C or C++. Less can be more.

But you can't take out everything. You need building blocks such as an idea about how types behave, and syntax that works well in practice, and some ineffable thing that makes libraries interoperate well.

We also added some things that were not in C or C++, like slices and maps, composite literals, expressions at the top level of the file (which is a huge thing that mostly goes unremarked), reflection, garbage collection, and so on. Concurrency, too, naturally.

One thing that is conspicuously absent is of course a type hierarchy. Allow me to be rude about that for a minute.

Early in the rollout of Go I was told by someone that he could not imagine working in a language without generic types. As I have reported elsewhere, I found that an odd remark.

To be fair he was probably saying in his own way that he really liked what the STL does for him in C++. For the purpose of argument, though, let's take his claim at face value.

What it says is that he finds writing containers like lists of ints and maps of strings an unbearable burden. I find that an odd claim. I spend very little of my programming time struggling with those issues, even in languages without generic types.

But more important, what it says is that types are the way to lift that burden. Types. Not polymorphic functions or language primitives or helpers of other kinds, but types.

That's the detail that sticks with me.

Programmers who come to Go from C++ and Java miss the idea of programming with types, particularly inheritance and subclassing and all that. Perhaps I'm a philistine about types but I've never found that model particularly expressive.

My late friend Alain Fournier once told me that he considered the lowest form of academic work to be taxonomy. And you know what? Type hierarchies are just taxonomy. You need to decide what piece goes in what box, every type's parent, whether A inherits from B or B from A. Is a sortable array an array that sorts or a sorter represented by an array? If you believe that types address all design issues you must make that decision.

I believe that's a preposterous way to think about programming. What matters isn't the ancestor relations between things but what they can do for you. "

"

Interface embedding is very simple. We've mentioned the io.Reader and io.Writer interfaces before; here are their definitions.

type Reader interface { Read(p []byte) (n int, err error) }

type Writer interface { Write(p []byte) (n int, err error) }

The io package also exports several other interfaces that specify objects that can implement several such methods. For instance, there is io.ReadWriter?, an interface containing both Read and Write. We could specify io.ReadWriter? by listing the two methods explicitly, but it's easier and more evocative to embed the two interfaces to form the new one, like this:

ReadWriter? is the interface that combines the Reader and Writer interfaces. type ReadWriter? interface { Reader Writer }

This says just what it looks like: A ReadWriter? can do what a Reader does and what a Writer does; it is a union of the embedded interfaces (which must be disjoint sets of methods). Only interfaces can be embedded within interfaces.

The same basic idea applies to structs, but with more far-reaching implications. The bufio package has two struct types, bufio.Reader and bufio.Writer, each of which of course implements the analogous interfaces from package io. And bufio also implements a buffered reader/writer, which it does by combining a reader and a writer into one struct using embedding: it lists the types within the struct but does not give them field names.

ReadWriter? stores pointers to a Reader and a Writer. It implements io.ReadWriter?. type ReadWriter? struct { *Reader *bufio.Reader *Writer *bufio.Writer }

The embedded elements are pointers to structs and of course must be initialized to point to valid structs before they can be used. The ReadWriter? struct could be written as

type ReadWriter? struct { reader *Reader writer *Writer }

but then to promote the methods of the fields and to satisfy the io interfaces, we would also need to provide forwarding methods, like this:

func (rw *ReadWriter?) Read(p []byte) (n int, err error) { return rw.reader.Read(p) }

By embedding the structs directly, we avoid this bookkeeping. "

--

http://commandcenter.blogspot.de/2012/06/less-is-exponentially-more.html

--

http://golang.org/doc/effective_go.html

--

"

upvote

waps 2 days ago

link

Not at all. Go gives (a lot of) the advantages of python and not many of the costs. I would say that Go dominates python, giving the advantages of that language with few of the costs. Anything you can do in python can relatively easily be done in Go. Replacing dynamic typing with reflection seems to work pretty well.

It does not similarly dominate C++. C/C++ cannot be replaced by something like go for the very simple reason that it wouldn't work. Go itself depends on a C runtime and a C compiler written in C, even ignoring the operating system it has to run on (which also cannot run without a C/C++/assembly core). The same goes for languages like Java, Erlang, Haskell, ... (Java is particularly bad, since it has a huge complicated runtime which is almost completely C++)

After all, who will garbage-collect the garbage collectors ? (this is a simplification, the garbage collector is a major problem, but not the only one. There's dozens)

reply

upvote

Shamanmuni 2 days ago

link

I agree with you, and some actual facts can be provided. Just look at the benchmark game between Go and C++ in quad-core x64: http://benchmarksgame.alioth.debian.org/u64q/benchmark.php?t...

In average, C++ is three times faster than Go in all the tests. These benchmarks aren't definitive but you can assume that in general the difference in performance is much less than an order of magnitude.

Now, look at the same comparison between Go and Python3: http://benchmarksgame.alioth.debian.org/u64q/benchmark.php?t...

Python3 is in average 20 times slower than Go. And I love Python but Go isn't that much harder to use. It's quite a nice language, actually.

So, the exodus from Python to Go is very understandable, you gain a lot of performance without sacrificing much. And I think there's room for performance improvements in Go, perhaps in a couple of years C++ developers will make the transition. But I think that the real C++ killer is Rust. Time will tell.

reply "

--

scottlamb 2 days ago

link

I program in C++ at Google. I won't make claims about "stellar". Here are a few Google-specific reasons Go hasn't replaced C++ for me:

Now for internal tools, where speed of development is more important, I wish I could magically switch everything over to Go instantly. Some of that stuff is in Python. Python's fragile in an always-refactored large codebase without 100% unit test coverage. The library situation is worse than Go's (few Python-specific libraries, and I hate SWIG). And while performance is less important for internal tools, Python's horrible single-threaded performance and GIL are a real pain. And finally, using Go for internal tools might eventually give me the comfort I need to start using it for more serving systems.

reply

--

" For low-level infrastructure projects that don't have very complicated business logic (e.g. caching systems, gateways between different RPC systems, etc.) but which run in many machines and must handle many requests per second, I'm sticking with C++. It's sometimes painful to troubleshoot race conditions around resource destruction issues, given that I use lots of async calls and threads, but still ... I don't think I'd feel comfortable investing significant efforts building this kind of systems in Go or even Java. That said, I'm trying to gradually migrate away from callbacks spaghetti and data sharing between threads towards ... specialized threads and message passing, a model far more similar to Go's. But I prefer C++ over Go and Java because it feels more predictable and, barring bugs, seems to have a lower operational cost. I don't want to imagine things like Bigtable or Google's other storage systems written in anything other than C++.

For higher-level backends with more business logic but which still must handle many requests per second, I lean towards Java. I feel that Java brings a few headaches for the production engineers that for some reason happen less in C++, such as having to tune the GC for performance and latency. Systems in C++ feel significantly more predictable, from my production perspective. But I'm willing to lose that predictability and switch to Java for the gains for the development team. I'm not sure I would be ready to switch to Go: I perceive the state of internal libraries on Go as trailing significantly behind those in Java. I guess this may change (or my perception may even be already dated).

Finally, for internal servers with little load, I'd already pick Go over Python. I come from a strong Scheme background and originally really liked Python, a very nice subset of Scheme. However, I'm coming to find projects in Python relatively hard to maintain. :-/ I feel that when Python projects hit a certain size (when a program reaches something like ~20 non-trivial classes, with some inheritance or interfaces, something like that), it becomes hard for new comers to extend it successfully. It may be just me, but I find that the lack of a canonical way to do things like interfaces means everyone has their own adhoc way to do this and ... things just get messy, or at least more messy, faster, than they do in the other 3 Google languages. It could also be the lack of type checks at compile time, I'm not sure. But I digress, all I meant to say is that I think Go may actually make things a lot better than Python in this regard, so I'm starting to use it in this area. "

-- w/r/t go vs. C++:

upvote

legulere 2 days ago

link

Two exemplary reasons why Go occupies a different space: - Mandatory garbage collection. Sometimes you need more control over memory than being able to use just one standard garbage collection. - No dynamic linking. Just think about what would happen if qt, webkit or llvm would be statically linked in in all programs using them.

--

upvote

acdha 2 days ago

link

That optional error handling still boggles me, particularly since http://golang.org/doc/faq#exceptions makes it clear that they undervalued the the other major family of problems affecting C programs: programmers not checking return codes in often-exploitable ways.

I can understand not going the full Java/C++-level overhead (although Python is a great example of how that doesn't need to be so tedious in practice) but it still amazes me that simply importing something you don't use is a fatal compiler error but the classic "res, _ = something()" responsibility shirk you see all over the Go world doesn't even get a warning.

Maybe Go 2.0 could make the compiler simply do the equivalent of inserting an `if err != nil { panic("Unhandled error at FILE:LINE") }` block after any statement which can return an error and doesn't already have a check. That'd satisfy the low-overhead, predictable flow-control crowd while making the language much safer for large systems in practice.

(n.b.: lest that seem like too harsh a condemnation, I actually rather like Go as a highly appealing alternative to C: the fact that they got so many things right — gofmt alone deserves a medal — makes the minimal error handling feel like a surprising oversight.)

reply

--

dbcfd 3 days ago

link

No RAII is one of the things that really bugs me about go. I can't just construct an object and have it be ready, I have to remember to call some other method. Rust's memory model is also better, since it's akin to shared pointers (which you know the lifetime of) rather than garbage collection.

--

upvote

luikore 3 days ago

link

A language which had aimed for system programing but not fast compared with C++, now aims for web programing but still harder to use than script languages, static typing but no generic types, forces boilerplate in several aspects (exception, assertion) but no meta programing ability to help fix it, weird design decisions dictated by personal experience, has Google helped marketing but still not widely accepted.

reply

--

jamesmiller5 3 days ago

link

There is cgo which allows one to call into C controlled code. That to me is best of both worlds, working in Go when I need to and memory control when needed as well.

reply

--

https://groups.google.com/forum/#!topic/golang-nuts/jVjbH2-emMQ

https://groups.google.com/forum/#!topic/golang-nuts/qTZemuGDV6o

--

https://talks.golang.org/2012/splash.article

--

"

upvote

samth 455 days ago

link

The claim that wanting to be able to abstract over types is the same as thinking that programming is about constructing taxonomies is one of the sillier claims I've seen recently.


upvote

Jare 455 days ago

link

His claim as I understood it is that the fundamental building blocks should not be types (what something is), but functional capabilities (what something can do). Type abstractions are fundamentally hierarchical (from an abstraction to multiple concrete versions), whereas capabilities are fundamentally about composition (I can do A, B and C).


upvote

Symmetry 455 days ago

link

Type abstractions are fundamentally hierarchical

In C++ or Java they certainly are, but Haskell's type classes, for instance, are much more similar to Go's interfaces.


upvote

zaphar 455 days ago

link

I don't think anyone is going to say that Haskell's type classes in any way fit the traditional notion of a type. It's more the exception that proves the rule in this case. "

--

upvote

enneff 455 days ago

link

The reason we didn't include exceptions in Go is not because of expense. It's because exceptions thread an invisible second control flow through your programs making them less readable and harder to reason about.

In Go the code does what it says. The error is handled or it is not. You may find Go's error handling verbose, but a lot of programmers find this a great relief.

In short, we didn't include exceptions because we don't need them. Why add all that complexity for such contentious gains?


upvote

jbellis 455 days ago

link

I'm with the grandparent. Every language that I've used that was created after C has included exceptions, and never once have I missed checking errno.

I can understand why, coming from a C++ context, you'd want to avoid them like the plague, but other languages (Python, Java, Smalltalk, ...) do a good job of making them a first-class citizen in the language, which saves a LOT of boilerplate typing. Seeing call stacks implementing a half-assed exception catching mechanism makes me sad. (Okay, Java only gets half credit here, since its checked exceptions inflict a different kind of boilerplate, but my larger point remains.)

Lack of exceptions is half the reason I'm not interested in writing Go code yet. The other half is that anything that seriously intends to replace C++ does need an escape hatch to allow manual memory management for when the GC's one-size-fits-all approach is simply a poor fit. For a language whose goal was explicitly to replace C++, this is an odd place to be tone deaf. I'm disappointed to see an attitude of "haha stupid C++ programmers don't understand that programmer efficiency is more important than CPU efficiency," instead of realizing that there are valid use cases where you do need to care about this and addressing them.

Which is unfortunate.

--

" On Mar 26, 2010, at 3:18 AM, Eleanor McHugh? wrote:

> Bold generalisation: an exception is nothing more nor less than the > statement at one level of scope that the current path of execution > has terminated (for good or ill) but that any decision about whether > or not to terminate the current thread of execution or the program > as a whole should be made at an enclosing level of scope. > > I really want the flexibility this understanding allows as it > results in cleaner code which is easier to maintain, especially when > combined with a helpful syntax. In the Ruby world we have > > begin > some_code() > rescue Exception > rescue OtherException? > ensure > end > > which whilst not perfect does allow different behaviours when > different exceptions are thrown (increasingly likely the further the > handler is from the scope where the exception is raised) and the > ensure clause would be equivalent to the current usage of defer in > go but only with regard to the code which might throw the exception.

This is exactly the kind of thing the proposal tries to avoid. Panic and recover are not an exception mechanism as usually defined because the usual approach, which ties exceptions to a control structure, encourages fine-grained exception handling that makes code unreadable in practice. There really is a difference between an error and what we call a panic, and we want that difference to matter. Consider Java, in which opening a file can throw an exception. In my experience few things are less exceptional than failing to open a file, and requiring me to write inside-out code to handle such a quotidian operation feels like a Procrustean imposition.

Our proposal instead ties the handling to a function - a dying function - and thereby, deliberately, makes it harder to use. We want you think of panics as, well, panics! They are rare events that very few functions should ever need to think about. If you want to protect your code, one or two recover calls should do it for the whole program. If you're already worrying about discriminating different kinds of panics, you've lost sight of the ball.

For the peculiarity of problem panic and recover are designed to address, a little clumsiness feels right.

The subject of this thread is not exceptions, but an exception-like mechanism. Even that pushes people's thinking too far into the direction we're trying to steer away from and I regret the error.

panic("these are not exceptions")

-rob "

--

"

re: Pike's view

You got me interested, Z-bo, so I spent some time going over the details and catching up on Go and this feature. Rather than waste that, even though I don't have an especially fancy, theoretical take on it, I'll say:

I think the design they put forward makes a mistake, based on their own stated criteria, the heart of which I find here (from Pike):

    Panic and recover are not an exception mechanism as usually defined because the usual approach, which ties exceptions to a control structure, encourages fine-grained exception handling that makes code unreadable in practice. There really is a difference between an error and what we call a panic, and we want that difference to matter. Consider Java, in which opening a file can throw an exception. In my experience few things are less exceptional than failing to open a file, and requiring me to write inside-out code to handle such a quotidian operation feels like a Procrustean imposition.
    Our proposal instead ties the handling to a function - a dying function - and thereby, deliberately, makes it harder to use. We want you think of panics as, well, panics! They are rare events that very few functions should ever need to think about. If you want to protect your code, one or two recover calls should do it for the whole program. If you're already worrying about discriminating different kinds of panics, you've lost sight of the ball.

In that case, I do not think that panic should be able to pass a value which becomes the return value of recover. Indeed, it's very awkward when they try to do so by having the declared type of that value be interface {} indicating a value you can't do much with without interrogating its type using the reflection facilities.

A simpler proposal might have been to replace panic with a parameterless abort and recover with a parameterless aborted that returns 0 or 1. Then we have a simple rule that every function whose body you can't inspect in detail may very well abort. It's fairly obvious that the feature isn't intended to be used for any "fine grained exception handling". And it's pretty hard to abuse the feature against its intended uses because a non-trivial use of aborted will stand out like a sore thumb under even light code review.

They seem to have wanted little more than a handy way to register and de-register dynamic "clean-up handlers", normally for events that end with program termination but perhaps in some server contexts. They wanted something like Posix exit and atexit but adapted to context. Why they threw in a parameter to panic is a mystery and probably a mistake.

Maybe a better name than abort and aborted, even, would be something like give_up and gave_up. In a command line context, the code that happens if gave_up is true might do something no more complicated than ensure an exit status of 2 (on a unix system). In an HTTP server, spawning little go routines to handle incoming requests, if the central loop hears that request handler gave up, it can at least tear down any central loop resources for that handler and send a 50x-whatever generic response. That seems to be about all they want it for and in that context, the parameter to panic and return value of recover seem like a kind of too-much-coffee thing. By Thomas Lord at Sun, 2010-04-04 02:25

"
login or register to post comments

---

my take: i agree with Tom Lord, the current way that Go does things is just exceptions under another name, except that 'defer' is more syntactically flexible than a 'catch' block, and except that the defer/panic/recover mechanism is slightly more confusing, esp. the way that all 'defers' are like 'finally' so you have to check 'recover' within your defer to see if you are panicing, and also because you cannot match the exceptions against a pattern in the 'defer' declaration like you can with a 'catch' block

i guess one good thing about them is that panic/recover is not a syntactic control structure.

---

this post seems to resonate with me a lot:

"

Revisiting Go

A little over 6 months ago I wrote a post about Go’s idiosyncratic error handling, but abandoned Go soon afterwards. I’ve always had a lingering feeling that I hadn’t given it a fair shake, and so I decided that the release of Go 1.0 was as good a reason as any to give it another try. After hacking away at a simple server for a week or so I’ve found a few things to like, but a lot that leaves me profoundly unsatisfied. So what’s to like?

I’ve always characterised Go as “C with garbage collection and closures”, and I still can’t really fault that description. Even when using goroutines and channels, it still feels like C. Possibly a C/Python hybrid. It’s very lightweight, it’s quite expressive and doesn’t overburden you with ways to get stuff wrong. You can be hacking away for a couple of days and feel like you really know the language, as opposed to C++ which still occasionally surprises me after 15 years.

I like the lightweight object system that seems to just work, although that could be because I’ve always preferred composition to inheritance when using OO type systems.

Go’s interface mechanism looks to be the functional equivalent of Haskell’s typeclass system, and seems to be a nice way of abstracting things. The jury’s still out for me on the implicit satisfaction of interfaces (especially as I can’t find a good way to find out if a type satisfies an interface short of throwing it at the compiler and seeing if it breaks), but I agree that it could be a useful property of the language.

Slices are a nice borrowing from Python (et al), and Goroutines and channels are a very nice way of dealing with concurrent problems.

The defer mechanism is a nice way of cleaning up after yourself. I think I still prefer C++’s RAII system, but defer is at a very nice point on the safety-vs-convenience continuum.

And coming from a day job using C++, Go’s compile times are fantastic. Mind you, being blazingly fast compared to a C++ compiler shouldn’t really be a challenge to a modern language. But what’s wrong with it?

The major thing that annoys me with Go is not so much a part of the language, but the “simplicity above all else” attitude expressed by the Go project as a whole.

Yes, simplicity is a good thing. But far too often “simplicity” seems like an excuse to be wheeled out whenever somebody asks for something useful but nontrivial. After a while it stops sounding like sage advice and begins to sound like a petulant child whining “But it’s haaaaaard!”. Trying to keep things simple is a laudable goal, but when you achieve it by pushing all the complexity you don’t want to deal with onto every other developer on the planet, it’s time to re-evaluate your design goals. Things should be as simple as possible, yes, but no simpler. Error Handling

I’ve also revised my opinion of Go’s error handling since writing my last post on it. It’s horrible. After writing little more than 500 lines of Go, if I have to write 1

if err != nil { return err }

(or its moral equivalent) one more time I’m going to hurl my computer across the room. Seriously. Contrary to my previously expressed opinion, it is underpants-on-head insanity.

Now, I do agree that people use exceptions to signal unexceptional situations far too often (Python, I’m looking at you here…). But even so, that is no reason to force every single Go programmer to write if err != nil { return err } on every second line of their program. I actually find it hard to reason about any Go function of more than 6 lines, because at least 4 of them will be the same bloody if err != nil { return err } nicely masking all of the important stuff that’s going on. Error handling via exceptions certainly ain’t perfect, but it’s better than this.

The whole return code idea also makes it nearly impossible to chain method calls or do anything even remotely succinct. If you like anything even remotely approaching a point-free style then Go is not your language.

On a side note, I’m all for multiple return values, too – but why not expand the concept into generalised tuples? Oh, that’s right: simplicity of implementation. Generics

I fail to understand how you can launch a modern, statically-typed language without support for generic types. Yes, I know it complicates the typesystem implementation, but you know what? I DON’T? CARE. By refusing to deal with this complexity once in the compiler, the Go team has forced each and every go developer to deal with it individually and separately. This doesn’t sound like the best tradeoff in history.

And yes, I also know that you can simulate a lot of what you’d normally need generics for using interfaces. That’s great. Now explain to me how passing around an interface{} is conceptually any different to, or any more typesafe than, passing around a void* (or an Object reference in Java). Consistency

Everything in Go is passed by value. Oh, except strings. And maps. Oh, and slices. Arrays are pass-by-value, though.

I’m sure there are compelling design reasons for all these these, but to the rest of us it’s just arbitrary and error-prone. Other stuff

My only other major gripe is that they’ve left out the one thing that would make channels truly useful: Discriminated Unions. With a discriminated union you could use a channel as a rich, type-safe communication channel (much in the same way you’d use a mailbox in F#), but there aren’t any discriminated unions in Go, so you can’t. Why? “Simplicity of implementation” again.

You can simulate this sort of thing using using interfaces, though. But thanks to the whole implicity-satisfied-interface thing you still have no compile-time guarantee about the range of types you’ll have to deal with, because potentially any type can implement your interface. Which means that you’re back to type-assertion-and-hope-for-the-best.

I’d also like to see some notion of const correctness (à-la C++), but that’s a pretty minor niggle compared to the rest of this rant. Conclusion

Writing all this makes me sad, because I really, really, really wanted to like Go. Much more than I wanted to like Scala. There are some lovely features in there, and there is something genuinely endearing about it’s lightweight, git-er-done style. But the more I use it, the more it feels like a squandered opportunity.

Maybe it feels better if you come at it directly from C, where its feature set would be overwhelmingly positive. Maybe I’m just too set in my ways to appreciate its novelty. Or maybe it just is a fundamentally a flawed design, and no amount of wishful thinking is going fix it.

I wish I liked it more than I do. " -- http://forfunand.wordpress.com/2012/08/12/revisiting-go/


golang has a strong convention that panic never crossed module boundaries, e.g. if you are using someone else's API you don't have to worry about panics

--

"

Why I’m not leaving Python for Go Posted on September 23, 2012

First of all, Go seems like a great language. It has an excellent tutorial which I joyfully went through and found:

    Go is Fast.
    Concurrent by design.
    Typed (important for JIT and IDE’s) but not cumbersome and ugly like C or C++’s spirals.
    Duck-type-esque interfaces.
    The defer mechanism is really nifty.

But there’s one problem I can’t live with. Which is a shame as I was eager to make the leap of faith in the name of concurrency. That problem is errors are handled in return values. 70′s style. "

--

upvote

rogpeppe 499 days ago

link

all inferno modules are written in limbo, which has a safe memory model, so modules can't access data they're not given.

check out some of the inferno papers, and the limbo language in particular, which is an elegant predecessor of Go, including some things that Go still does not have - the dynamic module loading being the most marked example, and the main reason i haven't yet ported the inferno shell to Go.


upvote

1337p337 498 days ago

link

I'd rather see Go ported to Inferno. :)


upvote

rogpeppe1 497 days ago

link

To make Inferno run Go efficiently, you'd probably need to add instructions to the Dis VM to do interfaces, channel closing, slice capacities and likely other things I haven't thought of.

To make Go run well under Inferno, you'd need to change the language to allow dynamically loaded modules (can we use the type compatibility model when we can't link everything together at once?).

These issues are doable, but even if you've done that, the performance trade-offs when writing a program to run in a VM are quite different from running natively - in particular, much idiomatic Go code would probably be dog slow under Inferno, because we're not steaming along at native code rates.

All that assumes we'd translate Go to Dis though. There may be alternative routes.

--

binarycrusader 36 days ago

link

(disclaimer: My opinion on Go is based on my own limited experience from writing a few private projects using OpenGL?.)

Go seems like an exercise in frustration to me at the moment for anything GUI or low-level OS-related.

Many GUI libraries use an object model that's difficult to map onto Go's heavily restricted interfaces, especially with a lack of generics.

In addition, interacting with popular libraries (such as libsdl or even OpenGL?) that use thread-local variables (TLS) means using ugly workarounds like this one:

http://code.google.com/p/go-wiki/wiki/LockOSThread

So I think it really comes back to the "right tool for the right job." For most command-line utilities, and for anything networking-related, Go would be my first choice.

But for anything that needs a modern GUI toolkit and uses OpenGL?, it would be difficult for me to justify.

Again, I love the model Go provides for programs and packages purely written in Go; it's only when interfacing with system-level components that I get cranky.

--

upvote

toqueteos 36 days ago

link

Not so relevant but for any of you who prefer Rust to Go and haven't tried Go, don't do it.

Go's std, visibility by case and gofmt, among other things will make you cry for using Rust.

I really hope Rust get's better with time and it really focuses on being developer friendly not just a bag of nice features.

--

bitcrusher 36 days ago

link

I can't be the only person who thinks Go is really interesting, but can't get over the 'package' hump... It's just too wonky for 'real-world' from my experience. For example, how do you create clear lines of separation with internal modules? If you want to use 'namespaces' then each namespace has to be it's own package, which then requires its own Repo that you have to 'go get'. There's unsustainable for a project of any useful size.

I must be missing something.


upvote

paddyforan 36 days ago

link

Packaging is both the best and worst of Go. I love the decentralisation, so there's no central directory of Go code. We don't have pypi, we don't have rubygems or npm, we have Github.

As others have pointed out, you can use directories as namespaces, which works nicely. But I've found, personally, that splitting my projects into separate repos for each namespace is actually beneficial. Helps keep my code clean and separated.

That being said, versioning is a real pain right now. The best thing I've found is to fork a repo when I decide to use it, then pull in updates as I adjust my code to work with them. Definitely not ideal, and if you have a lot of projects using the same dependency, it becomes a major headache.

There are some workarounds, and we've discussed the topic at great length on the mailing list. I think it's something we'll see a solution to in the next few years. But one of the things about Go that I really enjoy is that the core team is hesitant to push half-baked ideas onto the community. When we see a solution, it tends to be an elegant, clean solution that fits perfectly into the problem it solves.

In other words: yes, there are some problems. Yes, I do think they'll go away. Yes, I do think we'll need to be patient. Yes, I do think it will be worth it.


upvote

BarkMore? 36 days ago

link

Organize your packages in a directory tree. You can check the entire tree into a single repository. There's no requirement to put the code in a repository or use 'go get'.

The page at http://golang.org/doc/code.html explains how to organize code into packages.


upvote

Laremere 36 days ago

link

Each package does not need to be in its own repo. You can put packages inside subdirectories. The standard library is full of these, such as the net library. There is the net library itself, and there are subdirectories, including http, smtp, and more.


upvote

Game_Ender 36 days ago

link

Google has said they don't use 'go get' internally to manage dependencies, so they appear to agree with you.


upvote

gt384u 36 days ago

link

I would imagine this has something to do with having an existing package dependency management system company-wide and it causing friction trying to get the two to coexist.

I have similar issues where I work with trying to integrate an internal build system and rubygems. Our answer is to essentially mirror the gem version internally into our own repo. It's not the best answer I could hope for.


upvote

jbooth 36 days ago

link

They can all be subdirectories of the same main repo, yet import each other with paths like github.com/mainrepo/packagename.

The real thing that I haven't figured out a consistent solution to is how to manage versions. You could approximate version numbers with branches or tags, but nobody's put together a convention for it that works well with the tooling. That'll be important as go matures and has more libraries.


upvote

zeeboo 36 days ago

link

You can have multiple packages in one repo. All of our code consisting of dozens of packages is in one repo.


upvote

trevordixon 36 days ago

link

Exactly what I've been wondering. I've done `import "./[internal_module]" in some of my little experiments, but I've read that's unacceptable.


upvote

BarkMore? 36 days ago

link

Absolute import paths are preferred. If the import path for the importing package is [app], then import the package using `import "[app]/[internal_module]"`.

--

upvote

rybosome 36 days ago

link

I'm currently in a study group at work for Go, and I'm writing a few one-off personal tools with it. This is hardly serious experience, but it's enough to get a flavor for the language.

As the other answer to your question states, adopting a functional style in Go is technically possible, but practically it is so inconvenient as to be unusable.

EDIT: Take a look at this article, which explores developing a library that adds runtime-checked type parametric functions. http://blog.burntsushi.net/type-parametric-functions-golang

Choice quote:

"There’s no such thing as a free lunch. The price one must pay to write type parametric functions in Go is rather large:

Type parametric functions are SLOW. Absolutely zero compile time type safety. Writing type parametric functions is annoying. Unidiomatic Go."

--

Things we wish we knew

With all the compliments out of the way, you really do need a different mindset at times when dealing with Go compared to Python. So here’s a list of notes I kept as the migration took place – just random things that popped into my head when converting Python code to Go:

    No built-in type for sets (have to use maps and test for existence)
    In absence of sets, have to write your own intersection, union etc. methods
    No tuples, have to write your own structs or use slices (arrays)
    No __getattr__() like method, so you have to always check for existence rather than setting defaults e.g. in Python you can do value = dict.get(“a_key”, “default_value”)
    Having to always check errors (or at least explicitly ignore them)
    Can’t have variables/packages that aren’t used so to test simple things requires sometimes commenting out lines
    Going between []byte and string. regexp uses []byte (they’re mutable). It makes sense, but it’s annoying all the same having to cast & re-cast some variables.
    Python is more forgiving. You can take slices of strings using indexes that are out of range and it won’t complain. You can take negative slices – not Go.
    You can’t have mixed type data structures. Maybe it’s not kosher, but sometimes in Python I’ll have a dictionary where the values are a mix of strings and lists. Not in Go, you have to either clean up your data structures or define custom structs  Thanks to Ralph Corderoy for showing me how to do this properly (use the interface, Luke). http://play.golang.org/p/SUgl7wd9tk
    No unpacking of a tuple or list into separate variables (e.g. x,y,x = [1,2,3])
    UpperCamelCase is the convention (if you don’t have a title case on the function/struct in a package it won’t be exposed to other packages). I like Python’s lower_case_with_underscores more.
    Have to explicitly check if errors are != nil, unlike in Python where many types can be used for bool-like checks (0, “”, None can all be interpreted as being “not” set)
    Documentation on some modules (e.g. crypto/md5) is sparse BUT go-nuts on IRC is awesome, really great support available
    Type casting from number to string (int64 -> string) is different than going from []byte -> string (just use string([]byte)). Need to use strconv.
    Reading Go code is definitely more like a programming language whereas Python can be written as almost pseudocode. Go has more non-alphanumeric characters and uses || and && instead of “or” and “and”.
    Writing to a file, there’s File.Write([]byte) and File.WriteString(string) – a bit of a departure for Python developers who are used to the Python zen of having one way to do something
    String interpolation is awkward, have to resort to fmt.Sprintf a lot
    No constructors, so common idiom is to create NewType() functions that return the struct you want
    Else (or else if) has to be formatted properly, where the else is on the same line as the curly bracket from the if clause. Weird.
    Different assignment operator is used depending on whether you are inside & outside of function ie. = vs :=
    If I want a list of just the keys or just the value, as in dict.keys() or dict.values(), or a list of tuples like in dict.items(), there is no equivalent in Go, you have to iterate over maps yourself and build up your list
    I use an idiom at times of having a dictionary where the values are functions that I want to invoke given a key. You can do this in Go, but all functions have to accept & return the same thing i.e. have the same method signature
    If you’re using JSON and your JSON is a mix of types, goooooood luck. You’ll have to create a custom struct that matches the format of your JSON blob, and then Unmarshall the raw json into an instance of your custom struct. Much more work than just obj = json.loads(json_blob) like we’re used to in Python land.

Was it worth it?

Yes, a million times, yes. The speed boost is just too good to pass up. Also, and this counts for something I think, Go is a trendy language right now, so when it comes to recruiting, I think having Go as a critical part of Repustate’s tech stack will help.

--

jussij 45 days ago

link

> Go's type inference makes for lean code, but requires you to dig a little to figure out what the type of a given variable is if it's not explicit. So given code like:

   foo, bar := someFunc(baz) 

> You'd really like to know what foo and bar actually are, in case you want to add some new code to operate on them.

The gocode utility (which works with several editors) does exactly this.

upvote

rgarcia 45 days ago

link

Re: figuring out the signature of functions or the definition of structs, I've found godef useful: http://godoc.org/code.google.com/p/rog-go/exp/cmd/godef

The Go emacs mode has godef-jump (C-c C-j) and godef-describe (C-c C-d): http://golang.org/misc/emacs/go-mode.el

--

upvote

dodyg 44 days ago

link

Go is a nice language with awesome features except that it doesn't fucking allow unused imports/variables. It makes exploratory programming with Go annoying as hell.


upvote

im3w1l 44 days ago

link
    don't {
        print x for {x : unused variables}
        x() for {x : function from unused imports}
    }

--

upvote

dbaupp 45 days ago

link

Go doesn't have strong type safety either; I remember a recent story about a Go stdlib function "accidentally" calling an interface it shouldn't.


upvote

f2f 45 days ago

link

it wasn't accidental -- it was written on purpose by a programmer (a conversion from Writer to WriteCloser?). it was immediately acknowledged as an error and eventually may be caught by the standard code examining tool "vet".


upvote

pcwalton 45 days ago

link

What would the static analysis that "vet" is performing enforce to stop this? No interface-to-interface downcasts?


upvote

f2f 45 days ago

link

eventually, within the context of the `go vet` tool, the http://godoc.org/code.google.com/p/go.tools/go/types package may be used to analyze interface conversions ("I said I expected interface type A, but I'm using it as interface type B", which is unusual for go programs). i think that answers "yes" to your second question, but I'm not on the go team, so take my opinion with a grain of salt.

short of disallowing interface-to-interface casts, it is indicative of an error and should be vetted as such. the particular case I described earlier was covered by the Go 1.0 guarantee, so it had to be documented rather than fixed.


upvote

pcwalton 44 days ago

link

Hmm. Seems hard to do soundly in the presence of higher order control flow (e.g. pass the interface to a closure in a global variable—will the closure downcast it to another interface you didn't expect?)

--

upvote

DanWaterworth? 45 days ago

link

Go gives you Erlangs concurency model

There are a number of significant differences between Erlang's and Go's concurrency models: Asynchronous vs synchronous communication, per-thread vs per-process heaps, send to process vs send to channel.


upvote

zaphar 43 days ago

link

Go has asynchronous communication and synchronous communication.

And the other things you mention are in practice not significant differences. The model both use is based on Hoares CSP and the same general ways of using apply. Some of the specifics must accomodate differences but those are differences of implementation not the general model.


upvote

DanWaterworth? 43 days ago

link

No, they are different models [1]. Go does not have asynchronous communication. Bounded channels are still synchronous, because there is still synchronization happening; the consumer can't get too far behind the producer.

[l] https://en.wikipedia.org/wiki/Communicating_sequential_proce....

--

upvote

lbarrow 45 days ago

link

I'm glad to hear it.

Was there anything about the language that you found particularly forced clarity and modularity? I'm giving a talk comparing Go and Ruby next month and I'm curious as to what people with experience with larger Go programs find to be most helpful.


upvote

mdwelsh 45 days ago

link

The Go module system for sure. Also I really like Go's interface model (as opposed to Java or C++ classes) as it is more flexible and in some ways more precise. Note that I don't know Ruby so I can't compare Go to that...


upvote

danieldk 45 days ago

link

Thanks for the article, it was a very interesting read! Since this always comes up in Go discussions: did you miss generics in this project or was the lack of generics a non-issue?


upvote

mdwelsh 44 days ago

link

I haven't found that I needed generics so far, though I can see places where they would be useful.

--

The interface type issue is something we're still trying to figure out... Effective Go suggests ending single-method interface names in "er" but says nothing for multiple method interfaces. Maybe we'll add an "I" at the end of the names?

--

I am kind of upset that Google is not taking up Scala in a big way. The compiled code is almost always as fast as Java. The type system is much much better than Go and is capable of inferring much more than Go, with very advanced support for generics. Scala code is always much more compact than Go (true for most functional languages). Most features in other languages are simply implemented as a library in Scala. In particular Go channels are available as an Akka actor library in Scala. However Go has nothing comparable to Scala's Futures and Finagle libraries for async I/O support.

--

randallAugust 20, 2013 at 1:16 PM

As a stand-in for real "X implements Y" declarations, you can always add various cheap statements that cause a compile-time interface-satisfaction check if there weren't such statements already--different phrasings I was playing with last night: http://play.golang.org/p/lUZtDdP5ia

--

Anthony EdenAugust? 19, 2013 at 7:08 AM

I use both Go and Erlang, depending on my needs. I prefer Erlang's design and I adore OTP, however I'll be the first to admit that learning to think in Erlang was significantly more challenging than learning to think in Go, the latter being very similar to almost every other imperative language I've already used. Additionally when it comes to resource usage Go is pretty damn amazing (using very little CPU and memory compared to a lot of other languages I've used, like Ruby and Java) whereas the Erlang VM tends to use quite a bit of both RAM.

--

upvote

obilgic 28 days ago

link

The question i have been struggling to answer for few months now: Scala or Go?


upvote

trailfox 28 days ago

link

Pick Scala if:

  -You think a fusion of OO and functional programming is the way of the future
  -Runtime tooling and advanced instrumentation support matter to you
  -You like having first-class IDE support
  -You want to work for the likes of Twitter/Foursquare/LinkedIn/Amazon
  -Having tons of mature Java libraries available matters to you.
  -You don't mind investing time to learn the language

Pick Go if:

  -Memory footprint matter to you (and you don't want to pick C)
  -Startup time matters to you (and you don't want to pick C)
  -You don't want to spend much time learning the language
  -You like C-style error handling and aren't expecting something as expressive as a python or a ruby.
  -You don't mind limited IDE support
  -You want every post of yours to be voted up on HN regardless of content

upvote

Refefer 28 days ago

link

I would add a few more:

Pick Scala if:

    -You find value in strong static assurances.
    -You find value in generics.
    -You want to learn functional programming, not 'functional' programming (and you should).
    -You value succinctness.

Pick Go if:

    -Compile times matter (of course it does).
    -You find value in easier deployments (at the cost of potentially more deployments).
    -You don't want to learn D ;)

upvote

saosebastiao 28 days ago

link

Ha...love the last line. If I were in the market for something that solves the problems that Go attempts to solve, I would learn D. I just wish DMD was fully open source...it is awesome when you go to try a new language and all you have to do is "sudo apt-get ...".


upvote

asdasf 28 days ago

link

I believe that very small sounding issue has played a huge role in D being as uncommonly used as it is. It seems like a trivial thing, but the ability to just pkg_add languageX is incredibly important for adoption.


upvote

narwally 27 days ago

link

I don't think this is a small issue at all. I love reading through an interesting tutorial, and being able to apt-get install the language or libraries I need to work along with it. But if you make me jump through a bunch of hoops just to work through a tutorial, forgetaboutit!

--

" Structures in Go are laid out in memory identically to the same in C, allowing for potential zero copy use on either side (made possible given that addresses of data are fixed in Go — it is not a compacting GC, and doesn’t need the normal pinning functionality or expensive boundary crossing often seen in GC VMs like Java or .NET). There are concerns regarding who frees memory, and the lifetime of passed objects that *are* bound to be GCs, but those are topics for another time. "

example: http://dennisforbes.ca/index.php/2013/07/31/demonstrating-gos-easy-c-interop/

http://golang.org/cmd/cgo/

pointers are available however no pointer arithmetic: http://golang.org/doc/faq#no_pointer_arithmetic --

Go "Can call C code but can't be called from C code.". D "Interacts very well with C in both directions (version 2 can partially interact with C++ too)." -- http://www.llucax.com.ar/blog/blog/post/-522a870c

upvote

howeman 1 day ago

link

Go can do unsafe memory access, can't it? See http://golang.org/pkg/unsafe/

reply

upvote

georgemcbay 23 hours ago

link

Go can do unsafe memory access, but doing things like pointer arithmetic or casting of a memory blob to an arbitrary struct type is way more painful than it is in C/C++.

In a lot of contexts, this is a feature (makes terrible code smells in normal code easier to see), but depending upon the type of coding you do, you do sometimes find yourself wishing it were a bit easier when dealing with things like graphics APIs where you just want to lock a texture, update some bits in-place, and then unlock it. Sometimes what might be a couple lines of C code are 10s of lines of Go code where you either do crazy gymnastics with the unsafe package, or juggle things in and out of byte buffers.

reply


because this page is too long, continued on notes-computer-programming-programmingLanguageDesign-prosAndCons-golang