Hacker News new | past | comments | ask | show | jobs | submit login
Learn X in Y minutes where X = Go (learnxinyminutes.com)
199 points by realrocker on Sept 4, 2013 | hide | past | favorite | 88 comments



This is the way I like to learn a language.

Instantly, and progressively, by starting at the top and working through a single document that is both a reference and a guide.

Beautiful work.

Edit: One change I would make is to change p := pair{3, 4} into p := pair{x: 3, y: 4} which helps your declarations keep working in the future when you extend or modify the definition of `pair`.


> when you extend or modify the definition of `pair`.

Well, pair is an anti-pattern anyway, but what exactly would you add to "Pair"? "File not found"? :)

http://thedailywtf.com/Articles/What_Is_Truth_0x3f_.aspx


> Well, pair is an anti-pattern anyway

So cons is an "anti-pattern" as well, then?

Your comment is the most original dis of Lisp I've ever read. Bravo!


Go isn't a Lisp, last I checked. I am referring to "Pair" as a class/interface in an OO language. If you're using Pair to construct a linked list in a OO language, I salute you, but that's even more of an anti-pattern.

Pair is an OO anti-pattern because it's an arbitrary grouping of two things in a generic container, and you shouldn't do that in OO - classes are cheap. You should make a class that represents what the collection of those two things actually are. Typical field names in generic Pair objects are 'left' and 'right' or 'first' and 'second' which tells you nothing about the values you stick into them (except the types, if you're using a strongly typed language). If, as suggested in the example, the pair holds a 'x' and a 'y', a better choice could be to call it "Point" or even "Point2D". If it's a key/value pair from a map data structure it should be "Entry" with fields "key" and "value" - etc.


It is worth pointing out that the pair in question is indeed an {x, y int} (using Go's ability to elide repeated types, so this means both x and y are an int). The name may be poor, but since it also will in general use be paired with the module name, it may not be, either. "Pair" is a poor name, rationals.Pair might not be.

A truly generic Pair in go would be Pair{interface {}, interface{}}, and that would be a code smell. (Only a smell though, since it's possibly part of some larger construct that is basically getting around Go's compile-time type system and replacing it with a run-time check.)


It's generic, not in the Java-generics sense of the work, but in the sense that x and y are generic names - they don't tell you what x and y are. Yes, they are of type int, but are they apples or oranges? In the example, they are indeed just two numbers that we chose to keep together, although the name and field names suggest that the author has a point in mind.

rationals.Pair is still a poor name, it's a pair of what and for what purpose? Assuming you're referring to rational numbers, ratio struct {p, q int} is better.


In my C code I've been moving away from distinguishing primitive types by name alone, preferring to wrap them in single-element structs to get some type safety:

    do_things_with_fruits(apple_count_t apples, orange_count_t oranges);
instead of

    do_things_with_fruits(int apples, int oranges);
Which saves my ass when I wind up writing

    do_things_with_fruit(basket.oranges, basket.apples);

Note that at that point, I would say a generic pair type (not allowed in C but borrowing C++ syntax)

    pair<apple_count_t, orange_count_t>
is really no less informative than something like

    fruit_basket_t
and if it's easily defined (tuples + inference?) I see no reason to avoid it.


Yes, it's less informative, because you're missing the context of why the apples and oranges are lumped together in the pair. FWIW fruit_basket_t is poorly named too, since fruit baskets not things that can count apples and oranges (and nothing else), they are generic containers capable of holding all kinds of fruit.

I like the single-element structs. I've been finding myself doing that more and more in Java and I quite enjoy it. First epiphany was replacing bool with enums.


I don't think "fruit_basket" (or "fruit_counts", say, addressing your later point) really gives much more clue as to why you're grouping these things than does the templated pair. When there is an informative name, use it, to be sure. Occasionally there's not much more reason than "I am using both of these elsewhere. This is much more the case when things are used locally than when they are exposed in interfaces, to be sure.


> Go isn't a Lisp, last I checked.

Correct. I never thought it was and I never thought that you thought it was.

> I am referring to "Pair" as a class/interface in an OO language.

OK. I didn't get that from the context.


Too true.

Perhaps a var named "brillant"


Agreed. Diving in to examples is the best way to learn. I'm usually wary of these "Learn X in Y minutes" posts, but this one is great - maybe due to Go's succinctness?


MULTIPLE return values?! My mind is BLOWN. That's awesome. My work is all in Java and there are so many times on my last project where that would have been helpful (I know I can do it by creating a map or an array or something, but that's stupid and extra code that I shouldn't need to write).

I really hope to be able to write Go professionally at some point, it is a really nice language to write in.


Any language with tuples automatically gets "anonymous" multiple return values. (e.g. Python's `(1, "foo", None)`.)

(Go's multiple return values seem to just be simulating a single use case of proper tuples.)


Well, all I've written since college has been Java, so this is all new to me. I've only briefly checked out Ruby and Python, and not enough to really know that much about either language.


If that blows your mind, check out Haskell - you can dispatch on type of return value.


This is the first one-liner I've read that actually explains something useful I can do with higher-order type inference. Preventing errors at compile time doesn't count; doing something is run-time by definition.

Thanks.


Interesting, hadn't even considering looking at Haskell at all.


Care to elaborate?


As an easy to follow (though questionably idiomatic) example, consider the typeclass Default:

    class Default a where
        def :: a
and an unwise pair of instances:

    instance Default Int where
        def = 7

    instance Default String where
        def = "foo"
This means I can say:

    3 + def + length ("c" ++ def)
and get back 14. Note that which "def" depends on the type expected where it is used.

---

It's not limited to values, either. Consider the "return" function in the Monad typeclass:

    return :: Monad m => a -> m a
which is a function that takes something and returns that something wrapped in a monad. Which monad? Whatever is expected at the call site.

    [1, 2, 3] ++ return 4
gives us [1, 2, 3, 4] because list is a monad and return for lists gives a single element list, whereas

    putStrLn "foo" >> return 4
gives us an IO action that, when executed, prints "foo" and yeilds a 4.

---

A super complex example is variadic functions like printf, with the type

    printf :: PrintfType r => String -> r
PrintfType can be a String or IO (), giving you something like C's sprintf or printf based on the call site (which is itself cool), but it can also be a function that takes an instance of PrintfArg and gives back some new PrintfType (in an interesting intersection with currying).


You can do multiple return values of mixed (and non-predetermined) types in Ruby too.


Meh, not really. In ruby `return x, y` is just syntactic sugar for `return [x, y]`, ie returning an array. It's also an indication that you probably need a class.


I did not know that, but then I've only played around with Ruby a little bit.


multiple-value-bind. Old as Lisp


Wow, I'm not really interested in learning Go, but I was drawn in and made it all the way to learnInterfaces. Congrats!

One question: why have named return values (in learnMemory) if you return something different? If you didn't have a return statement at all, would the function return the final value of p and q?


>why have named return values (in learnMemory) if you return something different?

Probably to avoid declaring the function as `func learnMemory() (int, int)`.

>If you didn't have a return statement at all, would the function return the final value of p and q?

No, you have to do that explicitly. You can do `return`, though, which will return the variables whose names match the definition.


  > One question: why have named return values (in learnMemory) if you return something different? If you didn't have a return statement at all, would the function return the final value of p and q?
Kinda. You still have a return statement, but you may omit the arguments. See: http://golang.org/doc/effective_go.html#named-results


To describe what you are returning. An int tells you a lot less than status404s int.


Because the variable are already declared and ready to be used. Difference can be seen here → http://play.golang.org/p/IVPiw_2Uph


This looks very similar to the "easter egg" that the F# team put into visual studio.

When you create a new F# project you can select F# Tutorial and it generates a working project with a source code file that is an introduction to the language and its features.

I wish more IDEs did this.


My hungarian translation of it: http://learnxinyminutes.com/docs/hu-hu/go/


I'm tired of reading that this or that language/framework is "easy-to-understand" and "fun" to use. That's a subjective statement and mostly not true. Writing code is a means to an end, you want the output of a program not a bunch of lines, so I don't see how it could be fun (which is repeated over and over again in the official documentation and various slides and talks). And the concept of easy-to-understand is determined by the level of experience of an individual plus a whole lot of factors that are beyond us, not least of which is the sheer brain power.


What we mean: All of us on the team have a lot of fun writing Go. We often show each other little pieces of code that delight us. This seems to happen more when we work in Go than in the other languages we use (or have used), and so we attribute the fun-ness to Go. Pretty straightforward. I'm sorry you're so tired of it.


Wow, I didn't expect to get a response from an actual Googler, working on Go no less. Anyhow, I also remember when people were saying that Java was a fun language to work with, but I still have to find a single engineer who doesn't think of it as slightly more than a pain in the ass. It's all a matter of right-sizing expectations; using and objective language keeps you from being called out should those expectations fail to be matched.

That said, I've already professed my love for the Go language, which I consider the spiritual successor of the C programming language.


Java was fun to play with back in the early days. It was fast, had a VM, garbage collection and was OO. Sadly the fun was gradually removed over the years as a result of design-by-committee and a desire to try to solve some of its core issues.

Fortunately you can still have fun on the JVM, even though I prefer to avoid it these days given its current custodians.


I remember Java being fun, too. It's a shame its ecosystem got so severely over-engineered.


time for a new job?


I've come across many people that program as a means to an end. They don't enjoy it, it is just what they need to do to get the machine do what is fun, which is folding proteins, modeling ocean currents, driving the CNC, or what have you. For me, the actual application is fairly incidental to the programming and design part, which is where the fun is for me.


Is the documentation of go regarding the 'map' data type and the usage of the function 'make' out of date?

Because I can't see any difference in:

    package main
    
    import (
        "fmt"
    )
    
    func main() {
       m1 := map[string]int{}
       m1["a"] = 1
       fmt.Printf("%d\n", m1["a"])
    
       m2 := make(map[string]int)
       m2["b"] = 2
       fmt.Printf("%d\n", m2["b"])
    }


Compare with:

    func main() {
        var m1 map[string]int
        m1["a"] = 1
        fmt.Printf("%d\n", m1["a"])
    }
http://play.golang.org/p/JJQ7dSZLeA

which yields

    panic: runtime error: assignment to entry in nil map


Well, but this again works:

    type pair struct {
        x, y int
    }
    
    func main() {
       var p pair
       fmt.Printf("%d\n", p.x)
       fmt.Printf("%d\n", p.y)
    }
That's just wrong, that declaration and initialization behaves differently depending on the type.


If I could unilaterally make just one change to Go, I'd adopt C#-style nullable types, and change everything to be nonnullable without the extra sigil (or whatever, I have no passion about the syntax itself, just the feature). This would produce, say, map[string]string vs. map?[string]string. The first would not be permitted to be null, the second would.

It would be my choice because unlike a lot of other pet ideas which would radically change Go into a new language, I'm pretty sure this would have hardly any effect... excepting of course to remove the Billion Dollar Mistake from the next up-and-coming language.


Well, I'm more or less a programming language nerd and these kind of things just hurt =).

Just try to pass a 'map' or a 'struct' to an other function. The 'map' behaves like passed by reference and the 'struct' behaves like passed by value. If you want to pass a 'struct' by reference than you have to use a pointer to a 'struct'.

Seriously? What kind of ad hoc programming language design is this?


The zero value for a map is nil. The zero value for a struct is a valid struct whose fields are all zeroed. So for pair, that would be (0, 0). The zero value for a pointer is nil. If you had said `var p *pair`, then it would be a pointer to pair, and it would be nil, and you would get a nil panic error.


You can do:

  m1 := map[string]int{}
  m1["a"] = 1


Just out of curiosity, I ran format in play.golang.org, and it creates tabs with 8 spaces -- really? Is that what the command "go format" will do, too? Is standard go formatting tabs with 8 spaces?


I think it saves tabs in source files and displays as 4 space indents

http://golang.org/doc/effective_go.html?ModPagespeed=noscrip...


Both are equivalent and none is out of date. The first is just the short-hand literal syntax. Same with &Foo{} and new(Foo).


I think the 2 are generally equals. make is used when you want to initialize a map, slice, channel with a given size.

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


Thanks, that makes sense. So the 'make' function is more about the reserving of memory.


`make` also takes an optional size param that will let you pre-allocate a certain amount of memory if you know the size the map will grow to in advance.


Where exactly does the struct pair implement the Stringer interface? Is it when the String method was added to it? What if you have interfaces with similar names?


>Is it when the String method was added to it?

Yes, that is how you satisfy an interface in Go.

>What if you have interfaces with similar names?

The problem of interfaces with similar names is actually no different than the problems of, for example, packages or functions having similar names. It's down to you to avoid naming things in a confusing manner.


There is no "implements" in go. A type either provides the methods to satisfy an interface, or it doesn't. The name of the interface doesn't matter, and it can satisfy an many interfaces as it wants.


This is great. I suggest adding fallthrough statement in one of the switch cases.


Thanks! Inspired by you, just wrote the Romanian translation for Python. Maybe I'll do Go next :)

Seriously thinking about writing the Django equivalent of Michael Hartl's tutorial for Rails. Intimidating as it's a lot of work, over 100k words. Any people would like this? I really could not find a Django/Python equivalent.


That would be awesome, I've found the existing Django tutorial rather wanting.


> It’s not the latest trend in computer science

Uh, I think it is. I can't go three articles without seeing Go mentioned.

I'm going to get downmodded to gray, but either this tutorial is missing something or Go is way over-hyped. I saw nothing special in the language at all, I have no idea what all the obsession is about lately.


Really great way of learning the overall concepts of Go, but that code should have been gofmt'd!


This is very well useful and very well done, may I suggest to keep things consistent and actually use the syntax /* */ for multi-line comments?

EDIT: Also "// x == 1 here." should be "// x == 2 here.", those are 3 iterations.


> I suggest to keep things consistent and actually use the syntax /* */ for multi-line comments

Idiomatic Go code almost never uses multi-line comments, see: http://golang.org/src/pkg/net/http/request.go

> Also "// x == 1 here." should be "// x == 2 here."

No, it's correct. := declares a new variable. The newly declared, inner x shadows the outer x only within the block scope of the for loop:

http://play.golang.org/p/cP2dWyg0qw


Yeah, right, I didn't see the external variable. I might as well go ahead and delete the comment :P


I thought the comment "x == 1 here" could use a little more explanation (I figured it out, though, so maybe not.) I also found the function signatures confusing, especially the function with a local variable q that is nothing to do with its returned value q -- seems like a bit of a wart in Go. (Being able to assign return values vs. using return, as in Pascal, seems like an obvious reason to declare return values this way. Perhaps this feature exists but is not explained.)


I really love this format. Such a nice document.


I've been reading Chisnall's Phrasebook and Summerfield's Programming in Go books. Both are well done as far as descriptions of language features and standard lib go (covering 1.0), but are a little short on extended code examples.

And the usual amazon pricing: new $26, used $35.


Does it bother anyone else that Go supports the GOTO statement? I would've thought GOTO an obvious construct to leave out when creating a new language, to protect programmers from themselves? (See Dijkstra on 'GOTO statement considered harmful')


I think one of the string format examples missing the type formatter:

fmt.Println("it's a", i)

Should be something like:

fmt.Println("it's a %T", i)

See http://golang.org/pkg/fmt/ for more options.

[update]

Forgot they allowed to send pull requests; done :)


Println does not take format strings. You're thinking of Printf.


Yup, forgot to edit that :-/


That should be Printf. With Println you get "it's a %T 43". With Printf you get "it's a int"


Are you mixing up Println and Printf?


No, it's correct. It's meant to print "it's a 84".


Would't you expect either "it's 84" or "it's a(n) int|decimal|other_type"?

I figured they ment to do the latter, since the next print function outputs: "it's a string".


Since c is a chan int it can only be an int, no other type. You could directly write fmt.Println("it's an int").


I just tried it, both seem correct as fmt.PrintLn takes multiple inputs


Loved this. Can someone point to a relatively simple open source project written in Go that I can dig into to learn further?


    // If statements require brace brackets, and do not require parens.
Even if it is single line?


Yes, but you shouldn't ever see single-line ifs in go, because gofmt forces all of them to be multi-line.

This is one of the strong points of go (whether you like it or hate it): gofmt imposes the same formatting rules for everyone using the language. It's not a choice for you (or your project) to make. Period.


It's easy enough to never run gofmt, so there is still a choice. :) In fact, not running gofmt requires less effort than running it.


Not if you install the (excellent) GoSublime plugin in Sublime, it will run gofmt on every save.


Learning to use some newfangled editor and figuring out how to install plugins for it when the editor you've always used works just fine sounds like much more work than running gofmt. :)


Yes. After all that's the best style in C and other brace languages too.

This way you can't go wrong adding another statement inside the if.


Yes


Hm, would it not be where x := Go?


It's killing me that no one has made an APL version yet!


Need for Clojure.



That was one of the first languages covered by this site.




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

Search: