Live data from Hacker News

Alan Donovan and Brian Kernighan Answer Questions on Go

features.slashdot.org

91–100 of 110 posts

Re: Alan Donovan and Brian Kernighan Answer Questions on Go

#91

Earlier quoted context omitted.

For some things, yes. But there are a lot of potentially tricksy issues in doing that. Imagine a queueing library, for instance. You push something onto a queue created with library A, and then you want to read from library B (A and B are different version of the same library). Just about anything could happen (depending on how the library/language are implemented). You might get an error due to library B reading a d…

Making the version part of the type generally fixes these issues. Anything that could go wrong will nearly always go wrong at compile time.

It catches the error, but doesn't solve the problem, because now you can't write the program you intended to write.

Re: Alan Donovan and Brian Kernighan Answer Questions on Go

#92
post #6

> In general, Go strongly encourages being explicit about errors. That's completely incorrect: it's trivial (and common) to just ignore errors in Go: ok, _ := Foo() Checked exceptions don't let you get away with this kind of sloppy programming.

This is not a good example of how Go makes it easy to ignore errors (which it does in some cases, but not in that way). You had to acknowledge the dropped error syntactically.

A better example is the timeless:

    err := f1()
    if err != nil {..} // have to do this or compiler error

    err = f2()
    // oops, forgot to actually do anything about it!
Now, granted, `go vet` helps with this, but.. these kinds of things can be solved by the language proper in much better ways, and they should be type errors. Like rust's `Result` or Haskell's `Either`.

Edit: rust's result is especially nice with #[must_use]. This has saved my team from mistakes relatively frequently.

Re: Alan Donovan and Brian Kernighan Answer Questions on Go

#93

Earlier quoted context omitted.

I'm convinced this is the right approach: dependencies should be purely local to a module. The tooling should make it easy to detect you have a diamond, but let each module get what it needs. As generally everyone moves towards more continuous update and integration, at the end of the day it's all gonna be sha's anyhow.

But what do you do in a typed language? For example, suppose you have modules A and B, which depend on module C. Let's also say that module A has a function foo that return a value of type C.t, and module B has a function bar that accepts a value of type C.t. Now let's say that module A is depending on version 1.0.0 of module C, and module B is depending on version 1.5.2 of module C. Does B.bar(A.foo()) still work? I…

Good point. But if B.bar accepts an interface that C.t implements, then everything should work just fine and that should be the way to go on public APIs.

Re: Alan Donovan and Brian Kernighan Answer Questions on Go

#94
post #93

Earlier quoted context omitted.

But what do you do in a typed language? For example, suppose you have modules A and B, which depend on module C. Let's also say that module A has a function foo that return a value of type C.t, and module B has a function bar that accepts a value of type C.t. Now let's say that module A is depending on version 1.0.0 of module C, and module B is depending on version 1.5.2 of module C. Does B.bar(A.foo()) still work? I…

Good point. But if B.bar accepts an interface that C.t implements, then everything should work just fine and that should be the way to go on public APIs.

But what if the interface is defined in C? The interface has to live somewhere, and wouldn't it be versioned too?

Re: Alan Donovan and Brian Kernighan Answer Questions on Go

#95

Earlier quoted context omitted.

Making the version part of the type generally fixes these issues. Anything that could go wrong will nearly always go wrong at compile time.

It catches the error, but doesn't solve the problem, because now you can't write the program you intended to write.

Sure you can. Just run cargo update [-p] to get both of your dependencies on the same version and then recompile.

This won't work if the dependencies are truly depending on incompatible-at-the-API-level versions of the same library, of course. But there's no magic bullet solution to that—not handling versioning in the language/package manager doesn't make that problem any easier.

Re: Alan Donovan and Brian Kernighan Answer Questions on Go

#96

Earlier quoted context omitted.

It catches the error, but doesn't solve the problem, because now you can't write the program you intended to write.

Sure you can. Just run cargo update [-p] to get both of your dependencies on the same version and then recompile. This won't work if the dependencies are truly depending on incompatible-at-the-API-level versions of the same library, of course. But there's no magic bullet solution to that—not handling versioning in the language/package manager doesn't make that problem any easier.

Ah, so we're on the same page :). I definitely wasn't claiming Go's (non-)solution was the way to go, just that importing multiple versions of the same library doesn't solve the problem either. I definitely agree with you that this (Rust/Cargo's) is the best solution I've seen so far.

Re: Alan Donovan and Brian Kernighan Answer Questions on Go

#97

Go is as far removed from "extreme abstraction" as any other language I know. I don't know how it can even for a second be considered to have that desireable attribute. I, for one, don't like abstraction and power just because I'm a PL geek. I admit those are also reasons, but the truth is I use them to make my codebase smaller, simpler, and easier to reason about. For some, achieving that goal means writing for loop…

Agreed. There's abstraction and then there's abstraction. I might have been in the camp of Go enthusiasts if my experience with abstraction only included C++ or Java. But once I discovered Standard ML and OCaml I found out how wonderful abstraction can be.

Re: Alan Donovan and Brian Kernighan Answer Questions on Go

#98
Nice interview. Plenty of good details. I mean, there was this slip:

"The languages... are either long gone (PL/1) "

http://www-03.ibm.com/software/products/en/plicompfami

http://www.fujitsu.com/fts/products/computing/servers/mainfr...

http://www.iron-spring.com/about.html

Next he's going to be telling you COBOL is gone, too. Big time writers' definition of dead/gone in IT has always seemed different than general usage. ;)

Re: Alan Donovan and Brian Kernighan Answer Questions on Go

#99

Earlier quoted context omitted.

That's essentially the solution Rust and Cargo adopt. It occasionally causes strange errors like "expected Foo, found Foo" (where the first Foo was, say, Foo version 1.1 and the second was Foo version 1.2), but the compiler now detects this situation and tries to explain what's going on. I'm certain I'm biased, but in my experience it's been the most robust way to handle versioning that I've dealt with.

I proposed this in a Smalltalk newsgroup over a decade ago. The reaction? I was ridiculed. Imports naming specific versions would also allow better integration of code/build into a version control system. Actually, the entire OS should be built with version control in mind from the ground up.

No truly good idea will ever escape ridicule.

Re: Alan Donovan and Brian Kernighan Answer Questions on Go

#100
post #31

Earlier quoted context omitted.

Although a lot of people are pointing out (correctly) that it's trivially easy to ignore a checked exception, it does at least do two things: 1. Signal to the calling programmer that some error condition can occur. For example having to catch SomethingNotFound tells them that it's possible that Something might not be found. 2. You just put in your coding standards that you must do something sensible inside of a catch…

I've got a blog post on deck that basically says that just as Joel on Software proposed that the vast bulk of the advantage of scripting languages in the 2000s was garbage collection, I propose that the big advancement in error handling lately is simply the idea that it ought to be stuck in the programmer's face, and the exact manner in which it does so isn't really very important. By that standard, exceptions actual…

I like how Swift does it. There are no real exceptions, but error object handling to jump to the end is fairly easy. It reduces boilerplate and increases readability.

  do {
    try error-return-statement
    statement
    statement
    try error-return-statement
  } catch ErrorType1 {
    ...
  } catch ErrorType2 {
    ...
  }
It looks like exceptions, but under the hood error objects are being returned by error-return-statements, thus the explicit try keywords before them. It retains go error object simplicity, but keeps things readable and tidy.
Post reply on HN