Hacker News new | ask | show | jobs
by lmm 4366 days ago
Go may well be better for many apps than js or C++, but there are other languages out there. If you're looking for a new language it's worth considering more possibilities, e.g. see http://roscidus.com/blog/blog/2013/06/09/choosing-a-python-r...
3 comments

Incidentally, your link includes a great example of Go's error handling - which is inevitably what actually happens in languages without exceptions: errors are silenced and the program marches on, each step making less sense than the previous. It's a good talking point that you can always check error values - but it never really happens, in part because library designers try to avoid putting the burden on themselves and their users:

"Getenv returns the empty string and continues. Then Go somehow manages to parse the empty string as an empty JSON list and still continues. Then it tries to interpret the first of the user arguments to the program as the path of the program to run and execs that instead! Utter failure."

I happened to write about it just before Go came out here: http://yosefk.com/blog/what-makes-cover-up-preferable-to-err...

It seems that having exceptions in the language is a great predictor for libraries/built-ins barfing upon bad input vs silently producing garbage (as in JS's "undefined" string produced from undefined values and propagated, or Go's behavior above, etc.) For instance Lisp's NTH produces garbage and it predates Lisp's exception handling features whereas AREF was added later and indeed complains loudly, etc.

>It seems that having exceptions in the language is a great predictor for libraries/built-ins barfing upon bad input vs silently producing garbage

Maybe so, but having exceptions in a language is also a good predictor for the misuse of exceptions for purposes other than error handling. For instance, Python has the StopIteration exception to signal the end of an iteration.

Exceptions force API designers to decide whether or not a situation is exceptional enough to force a stack unwind on the caller's end. That's not something that's necessarily decidable from the point of view of the library.

For instance, say you have a function http.get(url). Should a 404 response automatically raise an exception? What about a 301? A network issue? Surely, for a crawler, a 404 is not exceptional enough to merit a hidden goto that unwinds the stack frame (by default). The designer of the http library cannot decide which status code is exceptional in every application.

It's similar with the results of SQL statements, file system access and all sorts of other IO related stuff.

It seems to me that the more complex a system is, the more it needs a very specific error handling strategy anyway and doesn't benefit from library designers' opinions about what is and isn't worth a stack unwind.

That said, I do hate seeing

  if err != nil {
    return nil, err
  }
on every other line of code.
> Maybe so, but having exceptions in a language is also a good predictor for the misuse of exceptions for purposes other than error handling.

Is that really a problem? I once hated such uses, but could never point why.

I think it's a problem for two reasons:

First of all, sudden stack unwind comes with a greater mental burden than regular structured code. Something implicit is happening that violates the expectations we have based on what we can see.

Secondly, consistency is always important because inconsistency forces us to think about things that we shouldn't have to think about, which lowers our productivity.

Don't worry, you won't see them that much since people will forget them.

That's the entire point of exceptions: never let an error get silenced.

Haha, no. You pretty much never forget them. And if you're worried, you can run a linter to find spots you've missed.

My go programs are 100x as robust as my programs with exceptions because Go actually forces you to think about the error path, not just the good path.

I could get behind the Go compiler to forcing people to write "_ = errReturningFunc()" instead of simply "errReturningFunc()" if they want to explicitly ignore errors.

In my recent experience, it actually bothers me a lot that funcs in the standard library would often rather return nil or the zero-value of a type than change the signature of the func to return (T, err). I don't really care that the docs describe that behavior -- that behavior is an error.

The thing is, compilers can force people to refer to error variables, but they can't prevent people from returning array[0] when i is out of array[]'s bounds, an empty list given an empty JSON string, etc. And forcing the former unfortunately only encourages the latter.
Returning (T, err) means that the function cannot be used as part of a larger expression. That's the main issue with this idiom in my view.
This is a feature. The statement might not succeed. You shouldn't use it as part of a bigger expression and assume it will work.

    t, err := foo()
    if err != nil {
        return err
    }
    bar(t)
this makes it totally clear that foo can fail, and if it does, then we won't call bar.

In languages with exceptions, this line just looks like

    bar(foo())
And then you can't tell that foo might fail and we might not call bar. Even if you wrap the whole thing in a try/catch, it's not clear.

This is why exceptions are an anti-pattern. They hide what functions can fail and what happens when they do.

The hundreds of high quality Go packages that do handle errors are an existence proof against your argument. Look at nearly anything on godoc.org.

You didn't cite that quote. I am certain whoever said it is doing something silly.

I did cite it - it's from my parent comment's link: http://roscidus.com/blog/blog/2013/06/09/choosing-a-python-r...

Are you saying that:

* Getenv doesn't return an empty string for a non-existent env var?

* Go JSON parser doesn't return an empty list given an empty string?

That you can handle errors without exceptions needn't have an existence proof... The question is how many errors are silenced. Stats will be hard to come by. But getenv and JSON parsing are pretty basic functionality and in most languages with exceptions they would not silence errors. (say, Python's json.loads barfs and os.getenv returns None which unlike the empty string will almost certainly lead to an exception if the code won't account for None). So it's a good anecdote in the absence of hard data.

Sorry, I missed the blog post. The Go code in the post is garbage. It doesn't check errors, which all good Go code does. (His plan to "add error handling only if the compiler told [him] to" is a bad idea.) I talk about the Go philosophy of error handling here: https://www.youtube.com/watch?v=dKGmK_Z1Zl0#t=27m10s

json.Unmarshal only returns an error value, so it never "returns an empty list". Instead you provide it with a []byte of the JSON data you want decoded and a pointer to the data structure into which to put the decoded data. If you don't check the error value returned by json.Unmarshal then you don't know if it decoded anything or not. That's why the author is left with an empty list.

In reality you'd add two lines to get the following code, which does not proceed past the failure in json.Unmarshal: http://play.golang.org/p/rYfwncjU2f

An aside: os.Getenv doesn't return an error value because it's a convenience that is mostly used in contexts where you don't care if the variable is set or not (in the shell you usually don't care either, you just test for != ""). If you actually care you can inspect the environment with os.Environ. But in this case it doesn't matter.

And here I thought you were talking about the first example in the blog:

    fmt.Println("Hello, world")
Now what happens when program opens a file and is run without stdout:

    ./program >&-
The file it opens is fd 1 and the program prints to the file instead of printing to the console. An odd case that has caused security holes in the past. In languages that fail on errors if they print anything before opening the file then they don't corrupt the file.

The default and laziest case for errors should never be to just hide them and continue on.

JSON parser does return an error on empty string.

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

If you use a `Decoder` instead you can check for the `io.EOF` error in that case.

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

Bam!
That post is a piece of garbage. He's clearly angling for a language like Rust or Haskell that has a very complex type system for preventing errors in the code, and no other language would succeed with the way he was writing code (which was completely inane). It's like he jumped in a plane, hit full throttle ahead and complained the plane crashed into the ocean instead of landing him safely in Seattle.
Getenv returns an empty string just like unset env variables act like an empty string on the command line. If it returned an error, people would complain it works differently than they're used to. You can't please everyone.
This blog post misses a very good Python replacement, namely Nimrod (http://nimrod-lang.org).
With the exception of speed, why does Python need a replacement?

I am using Python daily, and pretty much all performance problems (with my work) can be solved at the database or caching level. I get the impression that Numpy can produce reasonable performance for numeric problems.

In my experience developer productivity is of far more importance to business than language performance (obviously that depends on your domain). I do get asked to speed things up. But I get asked for "something working" by yesterday far more often.

The blog post gives the rationale; it's a combination of performance, a hope that static typing would make it possible to avoid a class of errors 0install had been experiencing, and wanting to ship native binaries with no dependencies - a pretty specialized use case, but one that 0install happens to need. (I'm aware that things like freeze exist, but they're not really first-class citizens, and the startup time - very important for a command-line utility - is not great).

In general I agree with you, I was a big fan of Python until I discovered Scala. But it's not a great fit for the 0install use case, and as you'll see from subsequent posts, switching to OCaml turned out to increase reliability and performance without sacrificing concision or development productivity.

> With the exception of speed, why does Python need a replacement?

Depends what you use it for. It's very good for a subcategory of problems, in particular prototypes or glue code. However, it leads to "use your test suite as a compiler" for large scale projects due its lack of static typing.

> In my experience developer productivity is of far more importance to business than language performance

You seem to be assuming that Nimrod would necessarily be less productive to work with than Python?

I do like Nimrod. It has been around for a while. Has good syntax, good semantics (things it operates with). Very fast.

Sadly it doesn't have the advocacy and hoard of developers supporting it and writing libraries for it. Certainly no big company with billions of dollars of revenue. It is unfortunate.

I feel like Google should have just taken it and used it as Go back then. I would have been a good choice.

There is also GNOME's Vala language. More focused on GLib but the idea is the same. C# like language compiles to C. Good performance. But again kind of fringe.

This "fringe-ness" in longer term translates in less libraries/frameworks. With Python if I am starting on something new, I know I can probably find someone that wrote a library related to it. Scientific computing, AI learning, graphics, GUI bindings, http parsing, WSGI middleware, strange protocol parsers, libraries on top of C libs (parse .pcap files) and so on. All are just one search and download away.

With Nimrod (or any new language) I am afraid I would be writing those by hand.

> There is also GNOME's Vala language. More focused on GLib but the idea is the same. C# like language compiles to C. Good performance. But again kind of fringe.

It seems like native imperative languages with automatic memory management has just fallen by the wayside as history has progressed, or have been relegated to niches. I wonder why that is.

Java happened. Sun took the JIT from SELF and everyone went JIT, repeating the VM everywhere from the P-Code days. Forgetting the native wave that followed.

However Java and C# do have quite a few native compilers available. They just tend to be forgotten on discussions about them.

> However Java and C# do have quite a few native compilers available. They just tend to be forgotten on discussions about them.

Are there any viable ones for java? It would be pretty cool if one could leverage some of the java ecosystem without having to include a vm. EDIT: I'm guessing that making command line utilities would be more viable, for example.

Most of them are commercial.

Excelsior JET - http://www.excelsiorjet.com/

Codename One - http://www.codenameone.com/

Atego Perc - http://www.atego.com/products/atego-perc/

JamaicaVM - https://www.aicas.com/cms/en/JamaicaVM

IBM J9 - http://www-01.ibm.com/software/wireless/wece/

Atego, Jamaica and J9 are not full native compilers, rather JVMs with support for AOT deployment, similar to .NET NGEN/JIT story.

Additionally Oracle has kept the Sun's work from the meta-circular JVM, Maxime as Graal and Truffle. Which may replace Hotspot for Java 9 or later and being used by AMD/Oracle for the GPGPU support.

This Java meta-circular compiler has support for JIT and AOT compilation.

https://wiki.openjdk.java.net/display/Graal/Main

EDIT: I forgot to mention the efforts of the guys behind RoboVM, currently the best way to target iOS with Java, http://www.robovm.org/

Go has a larger and more active eco-system for today's world.
Larger than...? It's comparable to Node's, certainly smaller than JS in general, much smaller than either python's or C++'s.