Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Go by Example (gobyexample.com)
301 points by A_Ghz on Jan 17, 2014 | hide | past | favorite | 124 comments


It's Friday gents! No excuse to set aside Saturday and Sunday, you can easily go through these examples in two days and grok it. Go is that _slim_, and that's good!

Guaranteed you'll find use for Go in one system or the other when you want easy deployment, fast development time and extreme speeds. :)

(Disclaimer: I love Go and I hope it goes mainstream in a big way in 5 years)


One nice thing these examples also do is make it really simple for detractors to point out what they don't like about it. My #1 gripe about go, for example, is this: https://gobyexample.com/sorting-by-functions

In a few other languages:

    // C++
    sort(vs.begin(), vs.end(), 
         [](const string& l, const string& r) -> bool { 
             return l.size() < r.size();
         });

    # Python
    vs.sort(key=lambda s: len(s))
When they fix this, Go might be the perfect language for me. Until then, I'm not touching it with a pole.


A few extra lines for sorting means you won't touch Go with a pole? That's a bit extreme. Do you really do custom sorting so often that this is a major problem?

There's plenty of other ways in which Go is shorter than the equivalent C++ (e.g. with type inference, or channel operations), but no-one's trying to claim that the shortest code wins.


> A few extra lines for sorting

It's not just sorting, it's any parametric polymorphism: generic operations or non-built-in containers (not just in the sense of "array", an atomic ref is also a container) for instance. Go only lets do these in verbose, clunky ways, and losing much of the type safety Go is supposed to provide (what with being a statically typed language)

> no-one's trying to claim that the shortest code wins.

No one is indeed, so that's an irrelevant strawman.


The post I replied to didn't explicitly say what he disliked about sorting in Go, and what "this" means in "When they fix this". I guessed that it was because the Go code is a bit longer.

What else could it be? I guess it's a little clunkier, and it's true that writing generic containers in Go isn't so great, but is that sufficient to elicit a reaction of "I'm not touching it with a pole"? Nobody claims it's perfect, but is it really sufficiently horrible to warrant that?


> it's true that writing generic containers in Go isn't so great

That's one hell of an understatement.

> is it really sufficiently horrible to warrant that?

A number of people obviously and repeatedly say that yes, a statically typed language without parametric polymorphism is sufficiently painful to use something else instead.


I'm curious what programs people are writing where parametric polymorphism is so crucial. I've written hundreds of thousands of lines of Go code (servers, web apps, data processing, etc.), and there's probably been maybe O(hundreds) of lines of code that were annoying or tedious because of a lack of parametric polymorphism.

My suspicion is that what's more likely is that lots of people don't want to consider writing their programs a little differently, and see that writing code with parametric polymorphism is the only acceptable solution to their problem. But maybe there's a whole class of problem (actual problem, not a sub-problem like "I want to implement a container") that I don't come in contact with.


"But maybe there's a whole class of problem (actual problem, not a sub-problem like "I want to implement a container") that I don't come in contact with."

i want to apply a transformation to every element in a collection

i want to select the elements from a collection for which some predicate holds true


Use a for loop. Stop thinking in LISP/Haskell/LINQ, start thinking in Go. Go is an imperative programming language. When in Rome do as the Romans do. Look at the code of the standard library to see how it's done.


It's not because it's unfamiliar that people are objecting; it's because it's suboptimal. Loops are harder to get right (try writing the loop to count down from 100 to 0 inclusive on unsigned ints), and they involve more boilerplate, obscuring the meaning of the code.

I don't think generics are an imperative/functional thing so much as an important feature of any statically typed system.


Those are both sub-problems.


of every software problem in history


I think a big part of the issue is that in Java and C++, you really need generics a lot more than in Go. Without templates, you would not have any easy way of doing maps and lists in C++. There are no builtin types for those like in Go. The way the type system works in those languages also makes things very difficult if you don't have generics.

Think about the sorting example I wrote earlier: https://news.ycombinator.com/item?id=7080562 If you were writing it in Java, is sort.Sort an interface or an abstract base class? Well, you can only "extend" one class (single inheritance only), so you would probably want Sort to be a Java interface. That means that you would always have to implement all three functions, not just one as I did, since Java interfaces cannot have default implementations. The comments indicate that most posters didn't even consider the idea that you could reuse the StringSlice functions. That method of easy composition simply doesn't exist in Java.

In general, generics get used a lot as a band-aid to avoid multiple inheritance in C++ and Java. You can't (or shouldn't, in C++) have your Foo inherit from both a (non-abstract) Bar and Baz. But you can certainly template on them. In C++, this kind of thing is called "traits" and Alexandrescu wrote a whole book about it. It's also why std::string is actually std::basic_string<char, std::char_traits<char>, std::allocator<char> >. In Go, you don't need all this... you just implement as many interfaces as you like and you're done.

So the Java and C++ programmers need time and an open mind to get up to speed. There's also another contingent of programmers that really just wants Go to be like their favorite strongly-typed functional programming language (Haskell, Scheme, SML, etc.) I like to think that these people will eventually give up. After all, nobody comes into every Lisp thread demanding that Lisp grow a type-checker. People have sort of accepted that that is not what Lisp is about and that there are other languages that will give you that if you want. Hopefully much the same will happen for Golang.


> It's also why std::string is actually std::basic_string<char, std::char_traits<char>, std::allocator<char> >

Actually, that's not a band-aid. It allows strings to be generic over allocators without suffering the performance penalty of a virtual call on every malloc and free. Go's solution would be to just take the penalty of a virtual call (as interfaces require virtual calls everywhere) and try to make it up with devirtualization optimizations and/or inlining.

> After all, nobody comes into every Lisp thread demanding that Lisp grow a type-checker.

That's because making a workable type system for Lisp is a very hard problem. By contrast, generics are well known at this point.


It's certainly true that Go doesn't currently support compile-time polymorhpism, and C++ does. In that sense, Go interfaces are not a replacement for C++ templates, as you correctly note.

However, compile-time polymorphism is a mixed bag because of how it tends to blow out the icache by generating many copies of similar code. Java does away with it completely, and nobody seems to be protesting. The Linux kernel is written in C, and nobody seems to be protesting that the lack of compile-time polymorphism makes his/her job impossible. Clearly, the decision as to whether to include compile-time polymorphism in a language is something reasonable people can disagree about.

With regard to a type system for Lisp: it's been done before. Check out Qi.


It is common in both C++ and Java for a class to implement several interfaces. So, one can accomplish the same kind of genericity in C++ and Java as one can in Go. Both languages still eventually saw enough of a need for a kind of generics to develop one.

There is a difference in Go, and that is you don't need to explicitly implement the interface. That is, you don't need to declare that you do. But I am unconvinced that is so different from implementing interfaces in C++ and Java that C++ and Java programmers "need time and an open mind" to understand Go idioms.

Don't get me wrong: I think Go is a neat language and culture. I like many aspects of it. And I know the stance the designers of Go have on generics: they would like to do them, but they can't think of a design that they like. And they don't consider it that important, so they've bunted on it. They may do them in the future. But I find it strange when people who are not the designers of the language argue against having them, in any form.


Java 8 will have interfaces with default implementations.

There are a few of optional type checkers for Lisp to choose from.


Parametric polymorphism is crucial in an API like jOOQ's:

http://www.jooq.org

In fact, it is one of the fundamental cornerstones of internal DSLs


I think the biggest advantage of parametric polymorphism comes from program analysis, not just increased capability. The notion of parametricity depends crucially on parametric polymorphism and is super useful. Parametricity is the property that says greater polymorphism means lesser variation in implementation. Highly parametric functions can be almost completely described by their types along meaning it provides incredibly high value as documentation.

The most common simple example is reasoning about the following pair of functions

    f :: a -> a           g :: Int -> Int
While (f > g) obviously, we can completely characterize the behavior of `f` while we know almost nothing at all about `g`. Up to non-termination, `f` must be `id` while `g` can be any recursive function on integers.

                g a = 3
    f a = a     g a = a * 2
                g a = if (a == 0) then 1 else a * g (a - 1)
If you hold in advance the triviality of this example, then we can already notice how much better documentation `f`'s type provides than `g`'s. We also can produce a list of properties and laws about `f`'s behavior that we cannot easily do with `g`. This means that I'm able to reason about `f` better and more confidently.

---

So we can take it up a notch. Consider Functors in Haskell. In order to avoid the full generality, let's just say Functor is the container interface and types like [], Set, and Map instantiate it. This gives them the function

    fmap :: (a -> b) -> f a -> f b
where `f` is specialized to the particular container of interest

    fmap :: (a -> b) -> [a]     -> [b]    
    fmap :: (a -> b) -> Set a   -> Set b  
    fmap :: (a -> b) -> Map k a -> Map k b      -- for some key type k
Now what can we reason about `fmap`s knowing only their type? Well, since the type `a` contained inside each container is unknown (or, to be more clear, any implementation of fmap must work for any type `a` and thus it's "unknown" in that we can't take advantage of information about it) then the only thing `fmap` is capable of doing is applying the first function to those types.

Furthermore, since every mention of `a` vanishes in the result of the function we know that each value in the input container must either be transformed by the input function or dropped.

    -- a specific example, not a general function
    fmap f [a, b, c] = [f a, f b, f b]
In this way, parametricity has pruned down the possibilities of functions that can implement `fmap` a great deal. If `a` or `b` were specialized to specific types then this would not be the case as we'd have a large variety of specific functions that could apply to each one of them.

    notFmap :: (Int -> Char) -> [Int] -> [Char]
    notFmap f [n1, n2, n3] = [f (n1 + 2), head (show n2), chr n3] ++ "hello world!"
Indeed, in order to ensure that type-checking `fmap`s behave correctly we say they must follow a few laws. First, they respect function composition and second they respect identity

    fmap g (fmap f x) == fmap (g . f) x
    fmap id x         == id x
And we only ever need to check the first one because the second one is already guaranteed by a more sophisticated parametricity argument.

    fmap f []     = []
    fmap f (x:xs) = f x : fmap f xs
---

The take away is that while you can definitely write something that "maps over containers" without using parametric polymorphism, you have a harder time taking advantage of parametricity to have the types provide guarantees about the safe behavior of functions. Types (and in particular type inference) can sometimes make things more expressive, but their primary power lies in making things easier to understand and reason about.


I'm going to note that even if Go added some kind of parametric polymorphism at this point, it still wouldn't have the same advantages as parametric polymorphism in Haskell and related languages. In Go, you can branch on types at runtime, so in a hypothetical generic Go, you could write something which appears to only ever return its argument, but does something special if the argument is of a particular type:

    func<T> id(t T) T {
      switch t := T.(type) {
        case int: return t + 1;
        default:  return t;
      }
    }
The ability to examine on types at runtime can enable some interesting programs, but it also means that you don't get guarantees like you do in languages like Haskell. An example of this biting someone in practice is this blog post[1], where the author reasoned that because a library function expected an io.Reader, all it would do is use the Read method as exposed by Reader. In reality, though, the function was able to branch on the type and determine whether it also exposed a Close method, and called that if it could. That kind of feature destroys the useful static guarantees provided by parametric polymorphism in other languages like ML or Haskell.

[1]: http://how-bazaar.blogspot.co.nz/2013/07/stunned-by-go.html


As a second addendum, while andolanra's comment is completely true, you can actually do type dispatch in Haskell using the Typeable machinery; however, the type system requires that you mark your types when this occurs.

    what :: Typeable a => a -> a
Here `what` is no longer guaranteed to be `id`. Instead, we can translate it like this

    what :: (a -> TypeRep) -> a -> a
where `TypeRep` is, as it sounds, a representation of the type of a returned dynamically. This means that we can do careful coercions to, say, be `id` unless `a` is `Int` in which case we add `1`.

Suffice to say that the degree of guarantee breaking that this kind of code invites makes Typeable a very specialized library that's almost never recommended without a "unless you really know what you're doing" kind of warning.


> In Go, you can branch on types at runtime, so in a hypothetical generic Go, you could write something which appears to only ever return its argument, but does something special if the argument is of a particular type

I'm not sure why you bring this up - nearly every language that enables reflection lets you do this, including Haskell[0].

No decidable static type system can ensure correct semantics for all use cases, including even badly written code[1].

[0] And you don't even need reflection to do this, per se; it just makes it more convenient.

[1] One could design a static type system that did, but it would be undecidable.


Parametricity is definitely not impossible to violate, but it's generally morally correct to assume it mostly holds in Haskell. Parametric polymorphism helps make that workable. Further, type classes help to tag code that does use reflection.

You can always get around things, but languages that make a strong commitment to keeping reasoning as a priority are qualitatively different from those that do not.


The last part is backwards -- when F is a functor, the only law we need to check is the identity law, but checking the composition law isn't enough. For example,

  fmap _ _ = []
satisfies fmap f . fmap g = fmap (f . g), but not fmap id = id.


Whoops, that's correct.


heh, interesting. as somebody mucking about with containers in Go, i also recognize it to be a sub-problem, and not one really worth having a language-level debate over.

perhaps odd, because i come from a crappily-typed PHP background. but if PHP teaches you anything, it's that you can accomplish just about anything with arrays (maps/slices).


With C++14 this becomes

    sort(begin(vs), end(vs),
         [](auto const& l, auto const& r) { 
             return l.size() < r.size(); 
         });
Even simpler.


What are the C++14 parts here? I haven't been paying much attention to the C++14 standards shaking out.

dfrey already asked about why begin/end are functions now.

I see auto being used inside the lambda. Was that not allowed in C++11? I got sucked into the "use auto everywhere" mantra, so I just imagined it was possible, but could easily see if it were not. That is probably the biggest decrease in size to the original code posted.

I see return type inference. That is one thing I have heard about being new in C++14. To be honest, I would rather see the explicit return type there. Inference is always nice, though.


> I see auto being used inside the lambda. Was that not allowed in C++11?

It wasn't:

http://stackoverflow.com/a/7709970/39622


Thanks for the reply. A comment under the accepted answer points to the wikipedia article on C++14, which has information on the new language and new standard library features [0]. I should have thought to check there first.

[0] https://en.wikipedia.org/wiki/C%2B%2B14


I omitted the return type (which is allowed for lambdas in C++11, and now arbitrary functions (with some conditions) in C++14) because it was a 1-liner. The bool return type can be deduced by the compiler automatically (or else it would fail to compile) and by the human by the operator < in use, the fact that it's being passed to sort, etc.


Why are "begin" and "end" functions now instead of methods on the iterator? Do they consider it better because now you can pass around functions, but not methods?


Because it works with arrays.


More than that, actually. Because of argument dependent lookup, this will work for any container that has begin and end functions in the same namespace.


Wow, yeah. This is pretty bad. The example doesn't even make logical sense. Why is ByLength a type? Shouldn't you just want to pass in an array?

Can any "idiomatic" Go programmers here give a better example of how you would do this?


You can't put methods on a type from a different package (or in this case a builtin type).

Instead of methods, you could of course write normal functions taking []string, but only methods can fulfil interfaces.


Can any "idiomatic" Go programmers here give a better example of how you would do this?

Sure.

sort.Sort(sort.Reverse(sort.StringSlice(vs)));


Oops, I looked at the Python code again, and it seems to be sorting the strings by their length, not reversing the sort order like I did. Sorry, I didn't look at it very carefully earlier. Anyway, the easiest thing to do is probably write a wrapper type around StringSlice using Go's anonymous struct syntax. You only need to implement LessThan, since StringSlice already implements the other two.

  type StringSliceByLength struct {
      sort.StringSlice;
  }
  func (s StringSliceByLength) LessThan(i, j int) bool {
      return len(s.StringSlice[i]) < len(s.StringSlice[j]);
  }
  Sort.sort(StringSliceByLength(vs))


This is the idiomatic way. It's actually the only way.


Hey, don't sell Python short!

    vs.sort(key=len)



Seems like he's set up quite a strawman of his opponents. He's made some very complicated and unfounded assumptions about people who criticize the language he likes.


Why not make an interface called iCompare that has a single function: compare, which takes an object of type interface{}. You can then implement that single compare function, attached to your type of choice. Problem solved.


You're only solving the example (painfully and incompletely as well, because now you can't sort your collection twice using different criteria, and I believe you can't sort objects you don't fully control since you can't add methods to somebody else's types — let alone a builtin, woe is you if you're attempting to sort maps). How do you handle a non-inplace sort? How do you handle implementing a treeset or a b-tree? A reference with compare-and-set semantics? Your interface won't save you from the devil of having to up- and down-cast all over the place and throw type safety into the trash.


1. You can add methods to other people's types.

2. My solution is not incomplete. It solves the stated problem exactly. If you need to sort the objects with a different criteria, implement the interface with whichever criteria you want. It's not so difficult. Generics will not save you from having to implement some kind of comparison criteria in other languages.

3. You can add more methods to the interface for an out of place sort.

4. If you need to implement a b-tree, add the methods to whichever type you need to sort appropriately.

It's not that big of a problem. You will not have to up and down cast. You only have to cast a type of interface{} to your type once and only once while you are comparing.

Is your problem ideological or specific to this example?


You can do this just as easily in Go as well, using reflection.


Yup! Here ya go:

    // Sort a slice of structs with first class functions.
    type Album struct {
    	Title string
    	Year int
    }
    albums := []Album{
    	{"Born to Run", 1975},
    	{"WIESS",       1973},
    	{"Darkness",    1978},
    	{"Greetings",   1973},
    }
    
    less := func(a, b Album) bool { return a.Year < b.Year },
    sorted := QuickSort(less, albums).([]Album)
For more fun things, see: http://godoc.org/github.com/BurntSushi/ty/fun


I have a blog post about this: http://alp.im/blog/go-linq/


You do realize that go has lambda expressions (anonymous, in-line, functions) right?


I do know this, but it is not sufficient to solve the problem we are discussing. (Or, to put the statement in the form of a question, how do you propose solving the problem using lambdas?)


You could pass the length and two functions for lessThan and swap.


lambda is redundant redundant here:

    L.sort(key=len)


I've learned enough 'Go' to write a small 3D framework in OpenGL, and after doing so, I have no desire to go back for most of my projects.

Making go-routines map to OS-level threads is a pain. And as much as I hate to trot out this old chestnut, the lack of generics is also a real pain.

I've gotten too used to having generic containers to go back, and the various hacks available just aren't worth it.

I still can't believe Go doesn't have some of the "basic" data types that older languages do such as Python's set() objects and the various operators.

That said, if was writing a small command-line utility for text manipulation or something else similar in scale that doesn't involve dealing with POSIX threading, I'd probably use it instead of Python, C, or C++.


You might like nimrod. Similar performance characteristics (or better) than Go, with Generics and meta-programming. Library situation is a little iffy, but there are full SDL and OpenGL bindings.


> Similar performance characteristics (or better) than Go. Nope. It's without a doubt 100% better. The GC is brilliant and realtime with deterministic properties. The community has outperformed C++ in ray tracers and games, Ada in text manipulation, etc. It's absolutely freaking blazing since it compiles to really tight C and takes advantage of the 50 years of C compiler research & design.

> Library situation is a little iffy. Libraries are hardly iffy. Any C code be called, and most c++ code can be called very, very easily and with little boilerplate. The standard library has a huge pure section, and for almost everything else it has dozens of bindings to C libs. And there are plenty of existing libraries in the babel library manager, such as SFML.


You're overselling it. While you can call C code, the interface is often not very Nimrod-y and it's not automatic, you have to declare prototypes, and there are absolutely holes in the stdlib - the lack of any arbitrary precision/bignums library for instance.


Are there any plans to add generics yet, or do I still have to write a bunch of extra boilerplate functions in order to sort a list?


What does yet another gripe about generics in Go have to do with a tutorial written by an independent party, one that is about features in the language today ?


Same reason most posts about MongoDB for a long time had complaints about Mongo's uh . . . let's say "relaxed" . . . approach to durability. Because proponents of the language/system/idea tend to promote an unbalanced view of its quality, its detractors need to be there to give people on the fence the right dose of the bad medicine.

Also, speaking only for myself, I really like the language and its ideas, but that one thing it's missing makes it too annoying to write code in it. I'd like to see more people aware of this deficiency and putting pressure on the language's owners to fix that problem, so that I can start using it.


I tend to think that you and the rest of "Generics or death!" crowd make a disservice to your own cause when you lazily come and repeat the same arguments. The rest gets bored and frustrated. Yeah, we get it, you like generics, it makes sorting stuff easier, and you're repeating the same stuff over and over again for our own good because you are true altruists, etc. Can we get over that?

If there's another thread which talks about good and bad points of the Golang design then, by all means, bash the language as much as you like. But if there's something you don't like it doesn't mean you get a free pass to repeat yourself whenever something with Go in the title appears in HN, whatever it is. Context matters, you aren't helping anybody, just trolling.


I would surely never take these complaints to a go-specific discussion board. That would be disruptive and rude. But this site is not that. I will react to stories on here as pleases me, and it pleases me to constructively criticize certain go design choices sometimes when the subject comes up.


You don't fool anybody, the subject didn't come up untill you brought it. The thread is about a site that shows examples of Go code and explains them, there's no discussion about the advantages or disadvantages of their design decisions, just matter of fact statements.

Of course, you can do whatever you want, but that doesn't mean that the rest of us won't probably look at your comments thinking "Here come the generics whiners again" and look in the other direction. You are alienating your audience with your indiscriminate repetition.

And, if the disclaimer helps you, I'd like generics in Go, they are useful sometimes. I just don't see the point in such zealotry over something which is merely convenient.


If only there were a workaround for not having generics, sigh.

But seriously, they may not implement generics out of spite or just to troll generics zealots.

Edit: remove ambiguity


I recently added the statement "type T interface{}" to a piece of code, as a private joke about the generics-in-go talk.

now my code looks like "func map(x []T, f func(T) T)" and everybody's happy :)


I firmly believe that when Go introduces generics, the syntax should look like:

type T1 generic type T2 generic

func Map(f func(T1) T2, s []T1) []T2


I'm not sure what you think I'm trying to fool people about. When Go comes up, I sometimes want to talk about how good I think it is. I hope you don't think I'm trying to hide that. It is pretty common on HN to comment on the merits of a thing when the thing itself comes up, even if said merits are not the specific subject of the link.

I can see that I am putting some people off with my commentary, but other people apparently got new information. Hard to say whether this is a win or loss on net. Time will tell, sooner or later.


Go generics and Mongo durability in the same thread. There is a trifecta brewing here...


Never underestimate the power of nitpicking in an HN thread.


It is amusing that you consider either generics or durability to be "nitpicks." One is a feature that is present in almost every static language of at least the last two decades, and another is a fundamental need of most database users, relational or not. Unless by nitpick you mean, "something I personally don't care about," I think your nitpick-classifier is a bit busted.


I don't consider the features (or lack thereof) to be nitpicks. They are valid criticisms, but they have had their own very extensive discussions, and we are all very aware of them.

Bringing that into a thread regarding a very cool set of examples regarding the core features of the language, isn't very constructive (at least IMO).


Ah. I see where our disagreement comes from. I do not believe that everyone is very aware of these issues. I believe go users are, but since this entire thread is evangelistic, there are people here who are not currently go users who are not aware. The exclamations of surprise at my provided example are proof enough of this for my satisfaction.


You do realize that people who look at the tutorial will discover the lack of generics on their own, right? And that if they share the "my God, lack of generics is a war crime!" attitude, they're bound to discover it literally right away?


This argument can equally be used to protest the saying of anything good about the language, since obviously people who try the language will discover this good thing quickly if it's useful. This is not a compelling argument to me.


It was in response mainly to:

> Go is that _slim_, and that's good! Guaranteed you'll find use for Go in one system or the other...

I'm all for learning new programming languages, but if you're going to learn a new programming language, why not use one that has new ideas?


Go does not have a lot of new features, but mostly a specific collection of convenient ones (static compilation, GC, etc.) Not sure how many new languages have truly new features, vs. how many are improvements in productivity (human and machine).

My point is that generics are not a new idea either, and plenty of languages offer them. Go is not meant (anymore) to replace C/C++/Java or even Rust. It will never have all the features of those, and probably should not.

But it seems impossible to have a HN discussion on Golang without part of the thread getting hijacked with "But generics!!!" This make no more sense to me than considering other languages incomplete because they don't generate static binaries.


> But it seems impossible to have a HN discussion on Golang without part of the thread getting hijacked with "But generics!!!"

Surely the exact same criticism could be leveraged at go proponents pointing out the exact same features they love every time, should discussions of Go simply stop until the next major release, and only new features (if any) be discussable?


> Surely the exact same criticism could be leveraged at go proponents pointing out the exact same features they love every time

This thread was started by someone complaining about lack of generics.

> should discussions of Go simply stop until the next major release, and only new features (if any) be discussable?

Why? Because there's no reasonable middle ground between "don't ever talk about Go until there's a release" and "hijack every thread about Go"?


Note my point in that line was not about criticism, but about thread hijacking.


Learning languages for their new ideas is fine, but there are other reasons you might want to learn a language. For example, because it is a good language for the specific kind of program that you want to write.

Go hits some sweet spot, apparently, since some people are happier using it than anything else they had tried. If no "new ideas" are going to help them, why should they chose more "innovative" languages instead of Go?


Because it's much faster than other languages at its difficulty level. Which can save a lot of money.


Hi hdevlance,

Generics actually have very little to do with sorting a list in Go. The example that the parent gave could be expressed in one line as "sort.Sort(sort.Reverse(sort.StringSlice(vs)));" No generics needed-- only composition. sort.Reverse is a wrapper interface which can be composed with any other sort.Interface to reverse it. It is typesafe, too-- there are no typecasts.

There are cases where generics would allow us to avoid typecasts, but this is simply not one of them. In many cases, what you want can be expressed via composition of types, and often (in my opinion) in a clearer way than by using lambdas, continuations, and callbacks. I suggest learning a bit more about the language and keeping an open mind.


If you say so -- I'm just going off of the examples given in the Go documentation on how to sort lists. If that's not the "Go-ic" way to do it, perhaps the documentation should be updated. If it is the "Go-ic" way to do it, then I think that my point still holds.

I also don't really have a lot of excitement about the prospect of solving problems by composing types in Go. For instance, to the best of my knowledge, Go does not have language support for sum types. It's not that I'd rather use continuations or callbacks -- I like powerful, expressive type systems to do, e.g., static enforcement of contracts -- it's just that I think that Go's type system isn't very expressive.

You're right, perhaps I'm totally misinformed about the language, and that lurking below there really is a way to do clear and clean things with types in Go. But everything I've seen points in the opposite direction, so at this point, though I have an open mind, I'd need more evidence to change my view.


The example we were discussing was written by Mark McGranaghan, and is not part of the go documentation per se. I think it's a nice little tutorial, but please don't confuse it with "the Go documentation."

Mark's point was that you can create an arbitrary wrapper type around another interface, so that the behaviors are composed. This is the most flexible way to do things. If there's already a wrapper type that does what you want, however, then you can simply use that. They are both examples of composition, and both "the Go-ic way to do it."

Composition is often overlooked by people who are fixated on other ways of doing things, like inheritance or generics. But in many ways, it's cleaner, since composition allows you to link together many modules without peeking inside.

It's funny that people have accepted the reality of dynamic languages, but can't seem to "get" the idea of statically typed language without generics. There isn't a troll comment in every Python article that it would be better with static typing. Type systems are a little bit like body armor-- development can be slower, but in exchanged you get a little more confidence in the program. People have accepted the idea of going naked, and the idea of medieval-style full plate armor, but the idea that you might want some type safety, but not go overboard often seems to fall on deaf ears.


Actually, the example I was referring to (see my comment elsewhere in the thread) is copied directly from the Go documentation here: http://golang.org/pkg/sort/

Composition is great. It does not solve the same problem as generics. The reason people can't seem to "get" the idea of statically typed languages without generics is because static typing without parametric polymorphism is a painful experience.

I'm not opposed to the idea of finding some balance between static and dynamic typing -- Clojure type annotations seem interesting -- it's just that I don't like the way Go does it.



"I resolve to recognize that a complaint reveals more about the complainer than the complained-about. Authority is won not by rants but by experience and insight, which require practice and imagination. And maybe some programming."

Of course, being Rob Pike, decreeing that authority and experience is more valuable than any (well-reasoned) complaints from other people is a very convenient stance for him to take; how many complainers are going to rival his résumé?


That's pretty much the boiler plate ad hominem.


Complaint<generics>("write a bunch of extra boilerplate functions in order to sort a list");


That's something for the Go creators to decide, but personally I enjoy the feeling of safety that comes with lack of generics. I have very few Go bugs at runtime and I think that explicitness plays a huge part in it.


What? I'm talking about stuff like this, copied from the documentation:

    func (a ByAge) Len() int           { return len(a) }
    func (a ByAge) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
    func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }
How does forcing me to type out (or copy-paste) an implementation of Swap increase safety? If anything, it just makes the code more error-prone.

And not having generics means that (as far as I can tell) people end up using a lot of `interface{}`. How does that increase safety?

More importantly, why would I want to use a language whose solution to the problem of "dealing with generic data" essentially boils down to "use a void*"?


Could you elaborate on how generics makes code less safe? In my experience, the lack of generics is much more type unsafe, as you often have to do casts at runtime (and handle them) when you have to implement your own data structures. In contrast, languages with generics handle those at compile time.


Not having generics reduces explicitness (because you don't specify the types of your containers) and reduces safety (because of all the casts and manual implementations of things like swap functions).


On the other hand, C++ templates sometimes make it hard to understand which function ends up getting called, what size a particular type actually has or what is an alias for what.

There are too many indirections to follow manually. All I know is that the compiler will select some function that has conforming types. The compiler is very very smart. It knows all the incredibly complicated name resolution rules and it will use each one of them.

Unfortuntately I'm not as smart. Even after more than 20 years of using C++ I sometimes fail to anticipate which function the compiler decides to use. And that is not safe.

In fact it is less safe than casting interface{}, because that will at least fail fast at runtime instead of silently doing something unexpected.


It is possible to have generics without having the specialization rules of C++.


You're criticizing ADL, not generics.


It's true that I'm not criticising generics in general, but ADL is only part of what makes C++ templates so treacherous.

I think Go needs some form of generics. Every statically typed language does. But after C++ I do understand the hesitation on the part of Go's creators.


Although no one seems to have this problem, golang (easier to google "golang" than "go") doesn't suffer from #ifndef guard problem which can make your large C++ app take hours to build http://talks.golang.org/2012/splash.slide#18. I mean if it were written in go it wouldn't take hours to build, and it would be clear which includes you really need. But people with large C++ code bases are probably--hey they have plenty of time to port code to golang while their large source code compiles.


Any language with module support has fast compile times.

Turbo Pascal 5.5 was compiling 34,000 lines/minute in 1989 in MS-DOS systems!

http://edn.embarcadero.com/article/20803

Compared with module based languages, C and C++ toolchains have been always slower.


Agreed. I've been programming Go for a while, and yet I still find this site invaluable. Because I context switch between many languages, I forget syntax a lot. Hmmmm... maybe I'm just getting old. Anyway, I find it much quicker to get the answer I need by looking at GoByExample, than by browsing the Go docs. Kudos to this site.

It helped me build this: https://github.com/joewalnes/websocketd/


I've started using Go on a personal project and this looks to be a great concise resource. Hopefully this'll give me some fuel to work on it this weekend!


This documentation is great, but one thing I tend to miss with examples like these is how to structure a project, deal with packages, etc.


GitHub is your friend here.

Here is a search for Go repositories with > 100 stars.[1] Every project will look different (the really big ones might be more complex then you need) but as you get deeper into the results you can find some great stuff.

[1]: https://github.com/search?l=go&q=stars%3A%3E100&s=stars&type...


That's a great idea, I hadn't thought of that.


"How to Write Go Code" shows you this, and is a good read: http://golang.org/doc/code.html


Thank you for your reference. I wish I could save it somewhere when I have time to go through it.

- Eggs should not be washed.


Agreed, the community does seem pretty loose on project structure--which is interesting since the language takes such strong stances on code structure.

Since your folder structure is also your package structure, with rmux[1] I tried to separate the project into dependencies, and use a package for each one. I like to also limit files to one class--but if you read through the default package files, the creators seems to go both ways.

One interesting thing that isn't mentioned often is how to handle the equivalent of #ifdef, through +build flags. This isn't the best solution, since all functions and their arguments are evaluated (even empty functions), but it does allow for the use of constants to wrap around cpu-wasteful debug-only areas/

[1]: https://github.com/forcedotcom/rmux


Another nice introduction is the Go Playground's Tour (http://tour.golang.org/#1).


The documentation format is a revolution of simplicity and comprehension. I can see this list format (and content) working for any/all languages.


Pocco[1]/Docco[2] is a good way to do this.

[1]: http://fitzgen.github.io/pocco/ [2]: http://jashkenas.github.io/docco/


I'm in the process of making a clone of the site for node.js [here](https://github.com/L8D/nodebyexample)


Throws a 404. Is it a public repo?


Yeah, I trashed pretty quickly because I realized I didn't have time to work on it, nor did I have the creativity to write good examples. It would have better suited to be about JavaScript rather than Node, and there are already plenty of better ways to learn good JavaScript on the internet. I might do 'Haskell by Example' which would be really fun, if I ever have time for it.


Love it, bookmarked. I've been hoping for something like this for a while. One of the most frustrating things for me about trying to pick up Go is a lack of 'guaranteed quality' examples of different basic operations, especially code that adheres to the latest spec.


Are there any plans to make this into something more interactive, like http://nodeschool.io/? Alternatively, do any of the interactive "Learn Coding" sites out there feature Go? I can't think of any.


The tour does this: http://tour.golang.org/


I am not personally interested in Go but I care about CSP. I will defently check out some of these CSP things and maybe try to recreate them in Clojure core.async.


Golang is good language. Cannot wait until it's generic.


It's about time! Go doesn't really have a good community or really practical examples. Glad to see someone took the time to put this together.


> Go doesn't really have a good community

What about freenode/#go-nuts? Seems like a very nice channel to me.


Thanks, I am going to use this for my go experiments.


Hey, nice site. FYI I can't copy and paste from the examples on the site without also bringing in the text on the left too.


Nice and clean. I like it. Maybe it would be nice to have comments with up/down votability ala Stackoverflow.


Such a nice UI. We all like fast food!

- Eggs should not be washed.


Very Cool - thanks




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

Search: