News for hackers. Stuff that matters.
Re:Go generics draft design: building a hashtable by malcolmgreaves on Wednesday June 17, 2020 @03:19PM (#23555544)
Since this post is illustrating the use of generics, why not go all the way with the get implementation to return an Option[V] type? It's a natural thing to do here. The return type is already a kind of sum type: it's either the value you want (non-zero value, true) or it's not (zero value, false). If the implementation uses the optional type, it'll become impossible to write code that uses the zeroed-out returned value incorrectly. Calling code must always explicitly check the returned Optional[V] value to access the value of V and continue or to perform some code to handle the not present case. As it stands, it's very possible to ignore the 2nd returned boolean value and write code that'll easily break.

Now, I can see why the author would _not_ want to do this, since this "explosion" of sum-typed things is present in all go code (e.g. the err := ...; if err == nil { ... pattern). So, it might be easier for Go programmers to see how they could use generics in their own code by re-using this pattern. However, I think this is a disservice to why generics are an incredibly useful construct in programming languages. They can be used to align code more closely with the semantics that the programmer wants to convey.

Re:Go generics draft design: building a hashtable by masklinn on Wednesday June 17, 2020 @03:30PM (#23555678)
> If the implementation uses the optional type, it'll become impossible to write code that uses the zeroed-out returned value incorrectly.

Since Go doesn't have sum types, it would most likely be possible: the option type would just be a reification of the MRV. At best it could panic if you try to get the value of an "empty" optional but now you've got a panic.

Re:Go generics draft design: building a hashtable by alkonaut on Wednesday June 17, 2020 @03:55PM (#23555966)
It’s always possible, even in a Rust Option you’ll panic when you extract the value with unwrap() without knowing that it’s valid.

What it does is preventing the accidental use of a missing value. You can’t pass the Option<T> on to a function taking a T without explicitly doing it.

[dead]
Re:Go generics draft design: building a hashtable by kelnos on Thursday June 18, 2020 @03:37AM (#23560716)
It's not even just rust; any language with functional features has an option/maybe type, and it's incredibly useful and a great tool to help ensuring your code is correct.
Re:Go generics draft design: building a hashtable by weberc2 on Wednesday June 17, 2020 @04:57PM (#23556653)
This doesn't add anything. You get the same protection in Go with pointers. For example, a `*T` can't be passed to a function taking a `T` without explicitly dereferencing it. The problem of course is that the type system doesn't guarantee that the pointer isn't nil when you go to dereference it, similarly your `Option<T>` doesn't guarantee that the option.Value is set correctly. You need sum types to provide this substantial guarantee.
Re:Go generics draft design: building a hashtable by TheDong on Wednesday June 17, 2020 @07:18PM (#23557999)
Pointers in go aren't a signal for optionality, they're a signal for stack or heap or optionality. They also don't play nice with interfaces.

Let's say I have a hashmap that returns a `&T` and I have a `func foo(s Stringer)`. Because 'Stringer' is an interface, it's possible for it to take both `T` and `&T` without a compile-time complaint.

In addition, I may wish to have my function `bar(t &T)` always take a pointer because I want the object to exist on the heap, or to be able to mutate its value for the caller.

Since pointers mean so many other things, they're not a good way to have a compile-error indicate optionality.

On the other hand, if my caller does `hm.Get` and gets an `Option<s: Stringer>`, it's clear what to do to pass it to foo, and if it's an `Option<&T>`, it's clear both that I want a pointer, and that it should be checked / unwrapped.

I agree that `T` / `&T` would be just as powerful as option types in go (without sum types) if pointers didn't already have other substantial meaning, and if they could sensibly interact with interfaces.

As it stands, I think you're off the mark though.

(note: All the asterisks are & because I dunno how to escape stuff on hn)

Re:Go generics draft design: building a hashtable by bradfitz on Wednesday June 17, 2020 @11:44PM (#23559610)
> Pointers in go aren't a signal for optionality, they're a signal for stack or heap

No, a pointer in Go doesn't mean that it's on the heap. The compiler keeps it on the stack if it's safe to do so, regardless of whether you're using pointers.

You can even write code like `t := new(T); t.Foo()` that very much looks like you're allocating on the heap, but it can stay on the stack, yet t is then a pointer to the stack.

Unlike C, you don't need to worry about the heap-vs-stack in Go. It's never even mentioned in the language spec as a concept people need to be concerned with. It's an implementation detail.

Re:Go generics draft design: building a hashtable by littlestymaar on Thursday June 18, 2020 @02:51AM (#23560464)
> Unlike C, you don't need to worry about the heap-vs-stack in Go. […] It's an implementation detail.

I'm really surprised to read that: yes a beginner can get his code working without wondering about stack vs heap (and that's one of the big reason why Go is easier to learn than Rust for people coming from non-system language), but as soon as you care about performance (which many Go users do!), you need to write code that reduces allocations to the minimum, because Go's allocator is really slow (compared to Java for instance). Interestingly enough, doing so forces you to think about the ownership and lifetime of your objects, like you'd do in C (or Rust).

Re:Go generics draft design: building a hashtable by weberc2 on Thursday June 18, 2020 @10:57AM (#23563654)
The great thing about Go is that you don't need to think about allocations or lifetimes until you care about performance. Memory management is optimization in Go, while in C, C++, Rust, etc it's required to get a program to compile (of course you can "opt in" to easy memory management in those languages by using some kind of GC library, this is vanishingly rare--presumably developers would rather just use a language that was designed for GC). If you're at the point where your Go code is so performance sensitive that you're optimizing memory more often than not, it probably makes sense to be using Rust, but these cases are rare for the kinds of applications I tend to write (which is sad because I actually really enjoy using Rust).
Re:Go generics draft design: building a hashtable by littlestymaar on Thursday June 18, 2020 @12:52PM (#23565003)
Absolutely (well at least for Rust, because for C it's not required for it to compile: the code will compile to some broken garbage that will either crash at runtime, be vulnerable to exploits or burn your hard drive, depending on the mood ;).

I just found surprising to see a core person of Go declaring heap vs stack allocation to be “implementation detail”. Because if it was, that would mean that my carefully crafted zero-alloc code could become full of allocations one day because the underlying implementation changed! Obviously they don't want their users to be afraid of that.

Re:Go generics draft design: building a hashtable by xyzzy_plugh on Thursday June 18, 2020 @01:25AM (#23560037)
This is not entirely true. You can exhaust stack headroom by calling functions with very large pass-by-value structs. In these situations, you are then forced to use a pointer, which results in heap allocation.
Re:Go generics draft design: building a hashtable by alkonaut on Wednesday June 17, 2020 @05:25PM (#23556967)
It guarantees that the value is set if the flag is saying it is set (that's the invariant of the type). It screams "check flag before accessing the value".

To compare with references which are also 0-or-1-thing effectively: A reference where you as a developer know it's never null but always a ref to exactly one thing is denoted "* T" and a reference to "one thing or null" is denoted "* T"! There is no difference in the types! so you can accidentally send one that is "0-or-1-things" to a method accepting a * T that MUST be a thing. Type system didn't help you document which case it was.

Options, apart from the annotation benefit it also helps making the syntax nicer in many cases, with e.g. "or()" fallbacks etc.

    let data = get_cached().or(load_from_disk()).or_panic();
Re:Go generics draft design: building a hashtable by weberc2 on Wednesday June 17, 2020 @05:51PM (#23557215)
I agree that there are semantic issues with pointers, but my point was to illustrate that you need sum types in Go to get any real benefit out of an Option type. If you need an option type and you aren't content with a pointer, you can use `(T, bool)`, but this is still a far cry from a real Option type.
Re:Go generics draft design: building a hashtable by alkonaut on Wednesday June 17, 2020 @06:16PM (#23557465)
I use Option<T> types very happily in C# without sum types. Most of what would be pattern matching can be done with just methods.

    Car c = maybeCar.GetValueOr(CreateCar()) // inline fallback 

    maybeDog.Do(d => b.Bark()) // only performs call if present

    Sailboat s = mybeBoat.As<SailBoat>() // none unless of correct subtype
And so on. With nullable reference types C# now has a builtin alternative to this, but it has worked we’ll for many years.
Re:Go generics draft design: building a hashtable by mdlayher on Wednesday June 17, 2020 @03:49PM (#23555890)
I like the idea, I just haven't given it a try with the new draft yet. Sounds like it's worth exploring at the very least.
Re:Go generics draft design: building a hashtable by malcolmgreaves on Friday June 19, 2020 @01:07PM (#23576477)
Cool! I'm glad you read my comment. I appreciate that you went through the effort to make a blog post (my comment is very low effort in comparison). I hope it came off as constructive.
Re:Go generics draft design: building a hashtable by ccktlmazeltov on Wednesday June 17, 2020 @07:16PM (#23557986)
I really wish Go would pursue sum types instead of generics
Re:Go generics draft design: building a hashtable by qtplatypus on Wednesday June 17, 2020 @08:19PM (#23558413)
Sum types and Generics solve different but related problems. So they are not really comparable. I would like to see both get implemented.
Re:Go generics draft design: building a hashtable by ccktlmazeltov on Wednesday June 17, 2020 @09:58PM (#23558999)
I'd say it's really hard to abuse sum types, I wouldn't say the same of generics.
Re:Go generics draft design: building a hashtable by qtplatypus on Friday June 19, 2020 @12:21AM (#23570943)
That is true. However sum types don't allow us to abstract over common code. For example a sum type wouldn't allow us to create a type safe channel "fan in" function that works for all channel types.
Re:Go generics draft design: building a hashtable by mrath on Wednesday June 17, 2020 @07:30PM (#23558087)
Sum types without generics will still be good enough? I think for things like Option/Result we need both sum types and generics.
Re:Go generics draft design: building a hashtable by ccktlmazeltov on Wednesday June 17, 2020 @09:58PM (#23558998)
I don't think you would need user generics for this to work.
Re:Go generics draft design: building a hashtable by OceanSpray on Wednesday June 17, 2020 @11:21PM (#23559490)
Without generics, sum types would have to be a built-in like maps and arrays.

With generics, discriminated unions can take on all sorts of user-defined shapes.

Re:Go generics draft design: building a hashtable by kccqzy on Thursday June 18, 2020 @03:13AM (#23560571)
Without generics, structs can take on all sorts of user-defined shapes. It's just that the types of its fields are fixed at declaration.

Without generics but with sum types, the sum types can still take on all sorts of user-defined shapes. It's just that the types of its various alternatives are fixed at declaration.

Re:Go generics draft design: building a hashtable by masklinn on Thursday June 18, 2020 @01:28AM (#23560050)
Non-generic sum types are useful but you can’t build an option or result type out of them, so the usefulness is somewhat hampered.
Re:Go generics draft design: building a hashtable by ccktlmazeltov on Thursday June 18, 2020 @03:50PM (#23567125)
But you don't need to, generic Option and Result types could be built as part of the standard library.
Re:Go generics draft design: building a hashtable by masklinn on Saturday June 20, 2020 @09:44AM (#23583900)
Yes but then you need neither sum types nor user-defined generics, you can just build-in whatever sugar is useful or necessary (possibly repurposing existing sugar e.g. the `select` statement).
Re:Go generics draft design: building a hashtable by ccktlmazeltov on Sunday June 21, 2020 @03:15PM (#23594548)
sum types are still super useful imo without generics.
Re:Go generics draft design: building a hashtable by shp0ngle on Thursday June 18, 2020 @03:56AM (#23560822)
Aren't the new interfaces from the draft, where you can list bunch of possible types, sum types?

edit: oh. no.

https://go.googlesource.com/proposal/+/refs/heads/master/des...

Re:Go generics draft design: building a hashtable by simias on Wednesday June 17, 2020 @03:31PM (#23555680)
I don't use Go myself but knowing its philosophy I wonder if they'll end up replacing the idiomatic "if err == nil" with an generic optional type even if they end up implementing generics in Go.

For one thing it would generate a massive amount of churn to upgrade existing code, and if you don't update you'll quickly end up with very ugly mixed error handling patterns. On top of that Go seems to really value compilation speed, so I suspect that they won't want generics "contaminating" interfaces all over the place only to do error handling.

I'm really curious to see (from the outside) how all of this is going to coalesce in the end.

Re:Go generics draft design: building a hashtable by weberc2 on Wednesday June 17, 2020 @05:54PM (#23557237)
Generics aren't the gap (multiple return values are already generic), but rather sum types. Sum types are what allow you to express that this is either None/Sum(T) (Option) or Ok(T)/Err(E) (Result) or Nil/Cons (List) or etc.
Re:Go generics draft design: building a hashtable by Groxx on Wednesday June 17, 2020 @09:58PM (#23559000)
Sum types are what give you compile-time safety over those options. Run-time safety and developer-intent-signaling is entirely feasible with just generics.
Re:Go generics draft design: building a hashtable by weberc2 on Thursday June 18, 2020 @10:50AM (#23563581)
Right, but as previously mentioned, Go's multiple return values are already "generic" and already signal developer intent. If you have a library method that returns (int, err) every single Go developer will check the err first before using the int. User-defined generics don't improve this use case.
Re:Go generics draft design: building a hashtable by Groxx on Thursday June 18, 2020 @04:09PM (#23567386)
Result types can, at runtime, by making the err case panic if the value is accessed, instead of just returning a zero value. They let you move past intent and into enforcement. Multiple returns are nothing but intent, and cannot be made stronger.

You can do that without generics, of course. But the developer overhead is large enough that it effectively does not happen, as you have to redo that by hand for every type. That's what generics bring - ergonomics good enough to stop using less safe workarounds (e.g. `interface{}`, multiple returns).

Re:Go generics draft design: building a hashtable by peter_l_downs on Wednesday June 17, 2020 @04:27PM (#23556320)
Take a look at the comment tree starting here in the Generics announcement thread from the other day for some discussion of Option[V] under the current proposal:

https://news.ycombinator.com/item?id=23545361

For what it worth, returning `(value, err)` is conceptually the same thing as returning a Result[V]. You can ignore the error case on a result / the None case on an option as easily as you ignore the `err` today.

Re:Go generics draft design: building a hashtable by MHordecki on Wednesday June 17, 2020 @04:44PM (#23556504)
> You can ignore the error case on a result / the None case on an option as easily as you ignore the `err` today.

The point of a result type is that you _cannot_ ignore the None case. Any method that would provide you with the value will also check that the error is not present.

In comparison, you can happily ignore `err` in Golang and continue with an invalid `value`.

Re:Go generics draft design: building a hashtable by peter_l_downs on Wednesday June 17, 2020 @06:19PM (#23557485)
I see what you're saying, and while I think you're right, I think a lot more of the value is in being able to use `Map()` /`FlatMap()` to be able to avoid thinking about error checking at all, as I laid out in the second part of this comment https://news.ycombinator.com/item?id=23549396 . The convention of returning `(value, err)` goes a long way towards enforcing the checking of errors; I have golangci-lint's `errcheck` linter enabled and basically cannot accidentally ignore the `err` values. In any case, Option/Result types are extremely useful and I'm happy that with generics the language will be able to include them!
Re:Go generics draft design: building a hashtable by estebank on Wednesday June 17, 2020 @07:25PM (#23558045)
For an ergonomic implementation of Result and Option you need to be able to represent tagged unions/enums/sum types, and having pattern matching makes dealing with them much more natural. These features are not planned for Go (yet?), right?
Re:Go generics draft design: building a hashtable by weberc2 on Wednesday June 17, 2020 @04:52PM (#23556594)
> In comparison, you can happily ignore `err` in Golang and continue with an invalid `value`.

I'm struggling to envision a Result type that requires you to be more explicit than `foo, _ := fallible()`. Seems like `fallible().Ok()` and similar are strictly less explicit.

Re:Go generics draft design: building a hashtable by hombre_fatal on Wednesday June 17, 2020 @05:10PM (#23556800)
To do anything with a Result/Optional, you have to explicitly get the value inside the box at some point. But the container abstraction also gives you a nice composition abstractions instead of the uncomposable top-level `val, err := do()` destructure.

Though perhaps not quite as compelling without real sum types. I'd have to play with Go's generic-typing sandbox more to form a stronger opinion.

Re:Go generics draft design: building a hashtable by kelnos on Thursday June 18, 2020 @03:40AM (#23560738)
> returning `(value, err)` is conceptually the same thing as returning a Result[V]. You can ignore the error case on a result / the None case on an option as easily as you ignore the `err` today.

No, you can't. If you want to pull the value out of a result and ignore errors, you have to explicitly do so. With `(value, err)` style error handling, you can write bugs by simply forgetting to check `err`.

Re:Go generics draft design: building a hashtable by cy_hauser on Wednesday June 17, 2020 @03:26PM (#23555631)
I'm not understanding this. Are you saying an Option[V] would reduce the number of lines of code (the explosion) that Go code uses for "err := ...; if err == nil"?
Re:Go generics draft design: building a hashtable by jerf on Wednesday June 17, 2020 @03:49PM (#23555894)
I don't think it would in Go. You'd still end up with

    result := ThingThatReturnsOption(...)
    if result.Error() != nil {
        // ...
    }
happening in general, or some other equivalent construct.

The main point of having Option in this case would be to make it so that where Go programmers normally write

    result, err := ThingThatMayError()
you can get precisely one of a result or an error. At the moment with the current calling convention it is possible to both return a result and an error.

However, I will say that while in theory this is advantageous (and I mean that seriously), in practice this is nearly a non-issue. I don't think I've ever had a bug because I had both things and misused them. I expect a dozen or more "Option" implementations to pop up nearly overnight once this is released, and for Go programmers to settle pretty quickly on not using it.

In non-Go languages, Options can have additional features that make them yet more powerful, such as chaining together optional computations in a way that makes it easy to shortcircuit whole computations, e.g., in Haskell:

     do
         x <- optionalFail
         y <- somethingElseFail x
         z <- moreMightFail x y
         return (extractFromZ z)
In Haskell, assuming the right definitions of the various functions, while that may look like it's not handling errors, it actually is, because the machinery behind their Option type (called Either in Haskell) is handling all the short circuiting. Go comprehensively lacks the features necessary to make that sufficiently pleasant to use that anyone will, though.

It is not unique in lacking those features, most languages are missing at least one thing to make it easy enough to use people will, but it does quite comprehensively lack them. It's not just a matter of adding this one little thing or that other thing, it'd be a whole suite of necessary changes, e.g., you might be able to write an .AndThen(...) function to operate on a Option type, but it's going to be too inconvenient to use, even post-generics, and even if you force it because it's the Right Thing to Do in languages that aren't the one you are currently programming in, it's still going to be a lot of disadvantages for not much advantage. Personally I don't value "Doing the Right Thing in language X while working in language Y" very highly, but some people seem to.

Re:Go generics draft design: building a hashtable by alkonaut on Wednesday June 17, 2020 @05:45PM (#23557156)
When you chain multiple things that can go wrong with Result[T,E] or Option[V] it should be possible assuming there are methods for chaining/fallback. E.g. opening a file, reading the contents, parsing the contents etc. If all those 3 return Result[T,E] and you want the overall result (parsed) T or the first error to occur, then you should be able to chain that.
Re:Go generics draft design: building a hashtable by Someone on Wednesday June 17, 2020 @04:03PM (#23556056)
I think they are saying using sum types will remove a source of errors (forgetting to check for error conditions).

I also think part of their remark is on ‘either’, not ‘option’, but that’s not important for the point being made.