HNHacker News
TopNewBestAskShowJobs

rogpeppe1

92 karma · joined May 21, 2012

[ my public key: https://keybase.io/rogpeppe; my proof: https://keybase.io/rogpeppe/sigs/CFNVd2VGl43MKBVo-xSB9wOzisGNCzs90-PlrVLVMU8 ]
submissionscomments
rogpeppe1··on Show HN: Multiplayer Snake in Go
i'd like to see a score system, and some kind of score ladder scheme where you only play people with on adjacent rungs of the ladder. then the folks that are really good don't end up playing against people like me who just crash into themselves all the time :-)
rogpeppe1··on Go: a nice language with an annoying personality
> Really, can't I indulge in a sloppy coding style while prototyping something

I sympathise with this view, but personally, as a full-time Go programmer, when I've been hacking up little piece of throw-away or explorative code, I've generally found that not-used errors have as often been helpful as annoying.

For example, the compiler might say "i not used", and I'll think "bloody compiler, let's delete the declaration", but I'll actually find that I've named a loop variable wrongly and that this would have been a hard-to-find error requiring a few edit-run iterations, and thus I've actually saved more time by doing what the compiler asked than if I'd been able to code sloppily.

If I do find myself adding and removing many print statements from within a package, I'll sometimes add a little function:

    func logf(f string, a ...interface{}) {
        log.Printf(f, a...)
    }
then as long as that function exists, I can add a logf call to any file in the package with no need to add and remove imports.

We can still start with mud, but it's good mud.

BTW we have warnings too, but they're generated by a different tool, go vet, which printf format checking and the like.

rogpeppe1··on Go Bloviations
A couple of remarks. FWIW my job is as a Go programmer.

The unused variable thing (particularly with local variables) has caught many bugs quickly for me. If there's a package that I find myself always adding and removing, it's easy to write a function that freezes it. For instance, for the debugging print example:

    func printf(f string, args ...interface{}) {
        log.Printf(f, args...)
    }
(I tend to use log for debugging prints rather than fmt, because it's guaranteed thread-safe, and it's easier to separate debugging prints from necessary prints at a later stage).

On the few occasions that I've really felt I lacked a ternary operator, I've just coded one up for the occasion:

    func either(cond bool, a, b string) string {
        if cond {
           return a
        }
        return b
    }
No big deal. I do find myself writing little functions as building blocks quite a lot, but I think this works well. Functions are a great building block, and Go is great for assembling blocks.
rogpeppe1··on Go Bloviations
If you've got a single package with 700 files (I'm pretty sure that's what he was testing), you're doing something wrong.
rogpeppe1··on Go Bloviations
actually, the Go tool compiles packages in parallel by default.
rogpeppe1··on Why I’m not leaving Python for Go
"So you can actually just write code like this: do val1 <- someValue val2 <- someFunction val1 val3 <- someFunction val2 someFunction val3 then, if any of the values return an error, it gets propagated to the end of the code."

I have seen this (the error monad) mentioned before as a "nice" way of handling errors, even with explicit error returns. I beg to differ - the error messages produced by such a program will be obscure, as all the context is lost - if the first call to someFunction fails, for example, there's not necessarily any indication that the error came from that call rather than the one after it.

Go's explicit error handling means that it's easy to add meaningful context wherever relevant - the error messages printed by such a program are likely to be considerably more useful.

In the end, adding error checks is doing useful work. Each error case should be considered individually, and I've often found it to be the case that it's useful to treat errors as regular values (for example by collecting a bunch of errors, or returning the most important error only).

I can understand the control flow of a Go function by inspecting it on the page (without a glance at the documentation). That's a huge plus.

rogpeppe1··on Why I’m not leaving Python for Go
You get a panic, which can be caught. http://play.golang.org/p/IaTAGizhrF
rogpeppe1··on A Tour of Go - Real examples of interfaces and concurrency
I've been using acme for a long time (previously vi was my editor of choice). One property of acme that I think isn't so obvious is the way that text becomes higher-order, in the same kind of way that S-expressions in Lisp are higher-order.

Much of the power of Lisp comes from the fact that the result of executing an S-expression is also an executable S-expression itself, so programs that write programs become trivial.

This property is true of acme too - we execute text with a middle-button click or drag, and the command that's executed can produce more text, which is itself executable text.

Thus I can extend the functionality of acme by writing tiny shell scripts that print useful commands. I can run a command that produces a list of stuff, use structural regexps to transform that stuff into an acme command, then execute that to transform something else.

The power of structural regexps combined with proper Undo/Redo is also great - rather than pipe something through sed or awk, I'll often iteratively build up an expression or set of expressions that perform some particular one-off transformation, rewinding and retrying as necessary.

I could go on!

Acme certainly isn't perfect, but it is wonderful.

rogpeppe1··on 5 Weeks of Go
An error isn't just about what went wrong - it's also about what you were trying to do when it went wrong, so you can do something appropriate. If you handle exceptions several stack frames up, then you lose that information. Doing things the Go way also means you can make nice error messages that reflect the task, rather than a stack trace that only makes sense if you know the source code.

Yes, every intermediary must agree on the error type - it's the language-defined error type, which defines a single method, Error, which returns the error as a string. There's no need for any further agreement.

Functions encapsulate errors. If I call a function, it is entirely up to me how I wish to handle that error. "The code that finds an error" is the code that calls the function. All the context is local. There's no need to know how that function first encountered that error - that's part of the implementation detail of that function. At every stage, we make error handling decisions based on local context. This makes the code more maintainable, because there is genuine separation of concerns.

If you really want exceptions (a classic example is a recursive descent parser where you don't want to check every call), you can use panic and recover, making sure that callers will never see it - it's a local contract only.

rogpeppe1··on 5 Weeks of Go
If you're going to do error handling right in an exception-using language, you really need to catch every exception close to its source, otherwise you won't know how to handle it properly.

I think Go's main approach is this: errors are not exceptional. Errors happen all the time, and should be considered part of the normal control flow of the program, not something to be shoved into a hidden compartment and dealt with later.

Thus using the usual control flow statements to check for errors is entirely appropriate, and it's immediately obvious when reviewing the code if someone has failed to do the right error checking.

BTW there is no os.Error type any more. There's a language-defined "error" type, which is an interface containing a single method:

type error { Error() string }

You only need to be intimately aware of what you're looking at if you want to take some different action based on the kind of error. This is actually quite rare (usually you care only that something failed, not how it failed), but when you need to, generally the calling package defines an error type that represents a particular kind or class of errors (for example http://golang.org/pkg/os/#LinkError)

rogpeppe1··on The Inferno Shell, an Introduction with Examples
To make Inferno run Go efficiently, you'd probably need to add instructions to the Dis VM to do interfaces, channel closing, slice capacities and likely other things I haven't thought of.

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

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

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

← PreviousPage 2 of 2