Live data from Hacker News

Typed nils in Go 2

dave.cheney.net

111–119 of 119 posts

Re: Typed nils in Go 2

#111
post #5

Earlier quoted context omitted.

Funny, I'm the opposite. The more experienced I've become, the more I've found that nil-punning is ultimately what I actually wanted. And I'm all for the idea that relational fields should be NOT NULL. I also fear that this doesn't really work for backwards compatible thinking. If I serialized some data down to disk before a field existed, I don't expect it to be there when I check it later. You can be tempted to thi…

I'm all for the idea that relational fields should be NOT NULL What if the data is actually missing? How else do you record that information?

If missing data is a valid value, pick a valid way to encode it. Null might work, but realize you could have to reason for the value. Actually missing, or just not collected it recorded.

I concede there may be no difference in those meanings.

Re: Typed nils in Go 2

#112

IMO the language made a mistake by allowing nil to satisfy any interface . When I write a function like func DoStuf(i ILoveGoer) { i.LoveGo() // Panic on nil } its hard to reason about because it doesnt look like you have a pointer, looks like you definitely have a value. IMO a nil should not be allowed for an interface. So the only way to create an interface var is in conjunction with assignment.

Nil interfaces don't necessarily panic when their methods are called, just when nil variables are dereferenced. This is because the receiver is just another argument. For example, if `i` were this implementation of LoveGoer it wouldn't panic on a nil receiver:

    type JoeLovesGo struct{}

    func (jlg *JoeLovesGo) LoveGo() {
        fmt.Printf("Joe Loves Go! jlg is %v\n", jlg)
    }
( Playground: https://play.golang.org/p/kanq_mSmaI )

Now, if this (admittedly uncommon) use-case is worth it's downsides is a different question, but that's how it's set up now.

Re: Typed nils in Go 2

#113
post #110

Earlier quoted context omitted.

I'm confused. Nil isn't an empty type. Why are you introducing them?

It goes like this: the claim is that dynamically typed "don't count for the comparison" because "compile time type checks are impossible". Even if we suppose that nil values or union types are useless at runtime (they aren't), it is not true that dynamically typed languages could not be analyzed statically. Not only compile time type checks are possible, they are sometimes expected to happen and already part of some…

(And that empty type is the one named by the symbol nil.)

The nil type is useful at run-time because it constitutes the bottom of the type spindle: just like in set theory the empty set is a subset of every set, including itself, the type nil is a subtype of every type, including itself.

This can be used at run-time; e.g. (subtypep nil 'integer) -> t.

We can't just exclude this value from the type domain on the grounds that it's static only. "Sorry, you don't get a bottom plug on your type spindle at run time ...". :)

Re: Typed nils in Go 2

#114
post #91

Earlier quoted context omitted.

I use Haskell examples because that's the language I'm most comfortable with, but, e.g. the Result type used in Rust is another example of how this can be done better. https://rustbyexample.com/std/result.html Ergonomic error handling or generics/parametric polymorphism aren't "Haskell methodologies". Go is one of a very small number of languages that have been designed in the last decade and lack features like this.…

Rust is my favourite language, I beg to disagree that its Result or Option types are more concise or require less boilerplate. The only real differences are that Rust's are type checked and harder to use.

Golang (may contain syntax errors):

    func readNumberFromFileAndDoubleIt(filename string) (int, err) {
        file, err = os.Open(filename)
        if err != nil {
            return 0, err
        }
        defer file.Close() // BUG!! this returns an error, but since we defer it, it is not going to be handled
        bytes, err := ioutil.ReadFile(filename)
        if err != nil {
            return 0, err
        }
        contents := string(bytes)
        i, err := strconv.Atoi(contents)
        if err != nil {
            return 0, err
        }
        return 2*i, nil
    } 
        
Rust (may contain syntax errors):

    fn read_number_from_file_and_double_it(filename: &str) -> Result {
      let mut file = File::open(filename)?; // file automatically closed at end of scope
      let mut contents = String::new();
      file.read_to_string(&mut contents)?;
      contents.parse().map(|i| 2*i)
    }
4 vs 15 lines, I think it's obvious which one is easier to read

Re: Typed nils in Go 2

#115
post #36

Earlier quoted context omitted.

this is called a bottom type in a type system. In JVM languages null and the throw expression return the bottom type. The only other option is to not have nil values.

Or to encapsulate a nil in a Maybe monad, so that you only have to deal with it in contexts where you explicity denote acceptance of nils. Then the type system won't let you get away with ignoring the possibility of a nil.

you can do a maybe monad without nil types by substituting nil with a single-valued singleton type

Re: Typed nils in Go 2

#116
post #114

Earlier quoted context omitted.

Rust is my favourite language, I beg to disagree that its Result or Option types are more concise or require less boilerplate. The only real differences are that Rust's are type checked and harder to use.

Golang (may contain syntax errors): func readNumberFromFileAndDoubleIt(filename string) (int, err) { file, err = os.Open(filename) if err != nil { return 0, err } defer file.Close() // BUG!! this returns an error, but since we defer it, it is not going to be handled bytes, err := ioutil.ReadFile(filename) if err != nil { return 0, err } contents := string(bytes) i, err := strconv.Atoi(contents) if err != nil { return…

Sure, in an unrealistic subset of cases, try! can hide the mess. But these aren't fair, and especially nor is your comparison.

Any practical code using Results soon ends up wanting to mix the errors from multiple sources. This requires a lot of boilerplate effort to make everything interop, and the machinery to reduce this is both complex and not standardised. If you don't go the upfront boilerplate-and-machinery route, things look awful.

And of course, if you use something else, like an Option, you're back to

    let foo = match bar() {
       Some(foo) => foo,
       None => return None,
    };
Go is much more consistent, and less pathological.

Your example is especially disingenuous, though. For example, you chastise Go with

    defer file.Close() // BUG!! this returns an error, but since we defer it, it is not going to be handled
but ignore the fact that this "bug" is nonoptionally hardcoded[1] into the Rust program. Which is it then?

Rust's error handling looks nice on fake examples, and manageable inside self-contained libraries. My experience of actually using multiple libraries is that Rusts error handling is a choice between tangly handling of nested errors or verbose attempts to early-exit.

[1]: https://github.com/rust-lang/rust/blob/master/src/libstd/sys...

Re: Typed nils in Go 2

#117
post #84
post #79

Earlier quoted context omitted.

Go has done nothing new for fast compile times, like any old timer coder will remember from Algol linage of compilers, with Turbo Pascal for MS-DOS being a good example of how long ago those fast compile times are known.

I'm sadly too young and ignorant of CS/technology history to be well-acquainted with the "old times". (It's something I intend to fix.) Even so, I'm all for praising the good things that Go does: if nothing, because of the tremendous mindshare it's getting and the number of people it reaches.

Codegear has kept some of the Turbo Pascal stuff on their museum site.

For example, Turbo Pascal 5.5 targeting MS-DOS was released in 1989 and was compiling 34,000 lines/minute.

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

This is just one example, there are plenty of other languages to choose from with a module based compilation model, only C and C++ toolchains have lousy build times given their textual inclusion model.

http://www.drdobbs.com/cpp/c-compilation-speed/228701711

So the only achievement of Go's compilation speed was making younger generations think it is something extraordinary.

Re: Typed nils in Go 2

#118
post #114

Earlier quoted context omitted.

Golang (may contain syntax errors): func readNumberFromFileAndDoubleIt(filename string) (int, err) { file, err = os.Open(filename) if err != nil { return 0, err } defer file.Close() // BUG!! this returns an error, but since we defer it, it is not going to be handled bytes, err := ioutil.ReadFile(filename) if err != nil { return 0, err } contents := string(bytes) i, err := strconv.Atoi(contents) if err != nil { return…

Sure, in an unrealistic subset of cases, try! can hide the mess. But these aren't fair, and especially nor is your comparison. Any practical code using Results soon ends up wanting to mix the errors from multiple sources. This requires a lot of boilerplate effort to make everything interop, and the machinery to reduce this is both complex and not standardised. If you don't go the upfront boilerplate-and-machinery rou…

Have you tried error-chain?

Re: Typed nils in Go 2

#119

Earlier quoted context omitted.

Sure, in an unrealistic subset of cases, try! can hide the mess. But these aren't fair, and especially nor is your comparison. Any practical code using Results soon ends up wanting to mix the errors from multiple sources. This requires a lot of boilerplate effort to make everything interop, and the machinery to reduce this is both complex and not standardised. If you don't go the upfront boilerplate-and-machinery rou…

Have you tried error-chain?

Kind'a proves the point, though, doesn't it? It's a complex and nonstandardized workaround of the kind Go explicitly tries to avoid.

As to specifically whether I have used error-chain, not really. I no doubt will in the future, but I'm not seeing Rust on my plate for a while.

Post reply on HN