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?