News for hackers. Stuff that matters.
Re:Why Go and Not Rust? by MuffinFlavored on Monday September 16, 2019 @10:34AM (#20984426)
Can somebody post an example of when you would use generics to solve a problem in the real world, and how that would be implemented in Go instead?
Re:Why Go and Not Rust? by otikik on Tuesday September 17, 2019 @06:04AM (#20993366)
Non-nullable types in are implemented using a combination of (powerful) enumeration types and generics.

Enumeration types in rust can be the usual ones you find in other languages:

    enum UserStatus {
      Anonymous,
      Registered,
      Confirmed,
    }
But their variants are not limited to names, they can be other things[1]:

    enum UserStatus {
      Anonymous,
      Registered{ email: Email, date: Date }, // a struct
      Confirmed(date), // a tuple
    }
Enum types can also be made generic. For example the Date type could be a parameter in the example above:

    enum UserStatus<T> {
      Anonymous,
      Registered{ email: Email, date: T }, // a struct
      Confirmed(T), // a tuple
    }

Which brings us to the Option type[2] in rust. It is an Enum with a generic type attribute, defined like this:

    enum Option<T>  
    {  
        Some(T), // a tuple with a single item of type T 
        None,
    }  
In rust, when a function says it returns an `i32`, it will always return an `i32`. It never returns `i32 or null`.

If you want not return an `i32` sometimes, you must specify exactly what else it can be. It is often convenient to return an Option<i32> instead. Then it can return `Some(i32)` or `None`. The difference with the "implicit null" from before is that the compiler will force you to "deconstruct the option safely", in compile time. You will never get a "Null Pointer Exception" in run time because of this.

For me, this is huge, and one of the reasons I like Rust more than Go.

[1]: https://doc.rust-lang.org/rust-by-example/custom_types/enum.... [2]: https://doc.rust-lang.org/std/option/

Re:Why Go and Not Rust? by killbot5000 on Monday September 16, 2019 @10:47AM (#20984575)
The simplest example I can think of is: Map(slice, func)

You can can implement this generically in Go using interface{} types and runtime type checking, but then you have runtime type checking failures.

A java/c++-esque "generics" implementation would be able to type-check at compile time.

Re:Why Go and Not Rust? by ori_b on Monday September 16, 2019 @10:55AM (#20984668)
You can implement it in Go using loops, and it's not significantly slower or more error prone.
Re:Why Go and Not Rust? by hauleth on Monday September 16, 2019 @11:02AM (#20984759)
Unless you want to implement that on top of your custom structure, like, for example, b-tree. Then you either need to write N different implementations for each separate tree or you need to use `interface{}`.
Re:Why Go and Not Rust? by ori_b on Monday September 16, 2019 @11:07AM (#20984800)
Yes, custom containers are one place that generics are very useful.

Go has the most useful containers built in. Most code I write doesn't need custom containers, even in languages with templates (I write far more C++ and Java than Go) -- so I find that I don't miss them much when I spend time in Gopherspace.

Re:Why Go and Not Rust? by NoodleIncident on Monday September 16, 2019 @01:09PM (#20986222)
The most egregious missing container is "set", coming from Python (as many Go developers do). After debugging, reviewing, and writing at least 3 different local implementations of a set, I can't wait for generics so that it can be written once and then never looked at again.
Re:Why Go and Not Rust? by mappu on Monday September 16, 2019 @04:49PM (#20988457)
I'm slightly surprised you found three different set implementations - `map[T]struct{}` is the idiomatic one i see everywhere.

I suppose it's not sorted or concurrent-safe?

Re:Why Go and Not Rust? by NoodleIncident on Monday September 16, 2019 @05:58PM (#20988984)
I mean that people make their own per-file or per-package set implementation for their service. There's no point putting it in a common utility package even within the same company, because it's specific to the type of what they're storing.

`map[T]struct{}` is only a type, it's not an implementation. You still need a handful of methods, that might be named inconsistently in each implementation by different developers. I will be happy when I never have to think about this again

Re:Why Go and Not Rust? by masklinn on Tuesday September 17, 2019 @07:37AM (#20993788)
> I'm slightly surprised you found three different set implementations - `map[T]struct{}` is the idiomatic one i see everywhere.

Set storage is not really the concern here, it's the set-theoretic operations which make sets useful (generally speaking, there are cases where all you need is the set being a set e.g. deduplication).

So I wouldn't be surprised that GP found several different implementations of union, intersection, difference, symmetric difference, subset, superset, …

Re:Why Go and Not Rust? by crimsonalucard on Monday September 16, 2019 @01:10PM (#20986230)
Yeah but you can't implement it generically.

That means for every conceivable input type and output type combination you have to have a new MAP or REDUCE function to handle it.

All Modern languages except golang and elm only require one implementation of MAP that can handle all type combinations.

It's called parametric polymorphism.

Re:Why Go and Not Rust? by excursionist on Monday September 16, 2019 @04:24PM (#20988203)
Elm has parametric polymorphism.
Re:Why Go and Not Rust? by crimsonalucard on Monday September 16, 2019 @05:18PM (#20988669)
you're right. My mistake.
Re:Why Go and Not Rust? by vbezhenar on Monday September 16, 2019 @03:53PM (#20987918)
Java is one example of modern language without proper generics. They have syntax sugar for Object references, but you can do almost the same with golang using interface{}.
Re:Why Go and Not Rust? by excursionist on Monday September 16, 2019 @04:26PM (#20988226)
I'm not sure what you mean by 'proper generics', but Java does have compile-time generics, ie List<T>, <A, B> B foo(A bar), etc.
Re:Why Go and Not Rust? by vbezhenar on Monday September 16, 2019 @06:00PM (#20988992)
Proper generics would allow you to parameterize with all types including primitive ones, not just Objects. Check out java.util.Arrays class as an example of why Java's generics are not proper: https://docs.oracle.com/javase/7/docs/api/java/util/Arrays.h...
Re:Why Go and Not Rust? by leshow on Monday September 16, 2019 @07:03PM (#20989438)
You've got a kind of made up definition of 'proper'. Java has generics, they don't have the best implementation of it I've seen, but I wouldn't call it 'improper'. It's just not a word that has any specific meaning in this context.

I seem to remember an article being written a while ago that found Java's type system, with the addition of generics, was unsound. Maybe that's what you're trying to say.

Re:Why Go and Not Rust? by vbezhenar on Tuesday September 17, 2019 @07:36AM (#20993781)
Java generics is a syntax sugar. They don't add anything substantial. You can use Object (or another bound type) everywhere you're using generic type. You only need to add few explicit casts, which are added by compiler anyway.

You can do exactly the same thing in Golang. Just use interface{} everywhere and add few casts where needed. That is Java generics.

Re:Why Go and Not Rust? by excursionist on Tuesday September 17, 2019 @09:55AM (#20995090)
> Java generics is a syntax sugar.

That's not true, Java generics are not just syntactic sugar.

> They don't add anything substantial.

They add parametric polymorphism and type safety.

> You can use Object (or another bound type) everywhere you're using generic type.

Only if you go out of your way to do so, and ignore warnings. This has less to do with how generics work in Java and more to do with the fact that Object can be explicitly cast to another type and vice versa.

> You can do exactly the same thing in Golang. Just use interface{} everywhere and add few casts where needed.

No, it's completely different.

Re:Why Go and Not Rust? by ori_b on Monday September 16, 2019 @03:46PM (#20987858)
Yes, I know what it's called. I have implemented generics in compilers. I still don't miss them in most go code, in practice.
Re:Why Go and Not Rust? by DocSavage on Monday September 16, 2019 @11:22AM (#20984953)
One example is n-dimensional arrays where each element could be a byte, uint16, int16, ... , uint64, int64, float, or even some custom vector. In a language with generics, you could describe the nD array more simply and the operators work depending on what was passed. The implementation can also be shared across a number of similar element types. In Go, though, you’d need to use interfaces and at some point you’d have to make tradeoffs about speed, simplicity of the implementation (e.g. hand-coding each element implementation) vs use of interface function calls to get properties, etc.

One example is to look at how Go handles the Image package vs other languages with generics. https://golang.org/pkg/image/

Re:Why Go and Not Rust? by mamcx on Monday September 16, 2019 @11:09AM (#20984828)
In fact with Go is even more obvious. Go don't have inheritance!

Traits is a way to solve it. But the problem is that in Go types are "too open". If you wanna constrain it more traits is not an elegant solution.

Generics is better in this case.

BTW: This is how is in Rust. Rust have traits but you fill the rest with generics.

I made a mini-go database command liner that I call from rust (so I get access to drivers that are not yet available in rust). Is much more boilerplate from some stuff that in the rust side is just Value<T>.

And type errors that Go have that rust don't.

I think that Rust & Go sharing some(most?) design principles (about how model types) make easier to see what each side make harder...

Re:Why Go and Not Rust? by jerf on Monday September 16, 2019 @03:04PM (#20987430)
You can prevent external packages from implementing your interfaces by adding unexported method names to your interface. Usually I'd call it an anti-pattern for interfaces, but I've done it a couple of times when there was literally no conceivable way for an external package to implement something conforming to a particular interface for various technical reasons.
Re:Why Go and Not Rust? by zenhack on Monday September 16, 2019 @05:20PM (#20988686)
Yeah, most "good" uses of this technique are kindof abusing it to substitue for some other feature the language doesn't have, the most common of which I can think of is combining it with type switches to get a poor-man's sum types (what rust calls enums). You see this a lot in code working with ASTs, especially.
Re:Why Go and Not Rust? by nine_k on Monday September 16, 2019 @10:41AM (#20984511)
You can solve any problem in a language without generics, such as Go, C, Fortran-IV, or any other Turing-complete language, including the Turing machine "assembly".

The difference is in speed of producing such code, amount of bugs introduced, ability to understand, modify, and evolve the code, etc.

Re:Why Go and Not Rust? by takeda on Monday September 16, 2019 @01:35PM (#20986558)
Don't know Fortran, but while C doesn't have generics you're getting similar behavior due to macros (for example min(), max() are macros) and weak typing. So the need of having generics there is much weaker.
Re:Why Go and Not Rust? by fwip on Monday September 16, 2019 @01:58PM (#20986797)
There are a number of tools (and the "go generate" construct) designed to support code generation. If you just want macro-level behavior, that's easy to do.
Re:Why Go and Not Rust? by takeda on Monday September 16, 2019 @02:48PM (#20987280)
Yes, and that's what I'm currently doing, but it is essentially just automating writing the same code multiple times. It still has issues, like having to remember to regenerate the code on change, or making harder to use IDE features to refactor. There are also some limitations it has.
Re:Why Go and Not Rust? by nine_k on Monday September 16, 2019 @03:16PM (#20987554)
> automating writing the same code multiple times

What would you call a language that forces you to write / use a preprocessor to introduce higher-level features?

Low-level. More specifically, low-level for the domain where you're working. C is admittedly low-level because it had to be minimal even for 1970 and close to the metal. Other languages usually enjoy less drastic design constraints.

Re:Why Go and Not Rust? by pjmlp on Monday September 16, 2019 @05:05PM (#20988580)
Yep, that is why some were already using PL/I and Algol dialects almost 10 years before C came to be, alongside Fortran, Cobol and Lisp.
Re:Why Go and Not Rust? by pjmlp on Tuesday September 17, 2019 @03:03AM (#20992441)
Just like Borland C++ 2.0 for MS-DOS did, before templates were started to be designed.

I let you research when it was released.

Re:Why Go and Not Rust? by pjmlp on Tuesday September 17, 2019 @03:01AM (#20992432)
Even C does have generics now (_Generic) even if quite limited.
Re:Why Go and Not Rust? by takeda on Tuesday September 17, 2019 @01:06PM (#20997495)
Neat, I had no idea.
Re:Why Go and Not Rust? by tgv on Monday September 16, 2019 @04:30PM (#20988252)
I have functions that have to deal with hierarchical data objects, JSON like things. All collections are identically typed (although they have optional fields, different length arrays, etc.), but there is more than one type. In order to write simple code, I wanted to write one function that gets them from the database, one to update a single record, one to notify of changes, etc. So my top-level type to cover all of these collections is map[string]interface{}, which in TypeScript would be Map<string, any>.

Then I ran unit tests, and sometimes a weird error cropped up. Turns out I made an error in an append: instead of concatenating two arrays, I added the second array as the last element of the first. That was a time consuming bummer.

This problem could have been prevented by two approaches: copying every function for every type, or generics.

Go is a nice language, which makes some tasks easy to implement, almost fool proof, but it still lacks in other areas.

Re:Why Go and Not Rust? by peterbraden on Monday September 16, 2019 @11:00AM (#20984733)
For a Math.max function you would normally have generic comparables and implement on that.

Go makes you implement max and min in all your codebases.

Re:Why Go and Not Rust? by takeda on Monday September 16, 2019 @01:37PM (#20986589)
Same with float, if you for some reason need to use float32, tough luck. Makes you wonder why even have types that aren't supported by the standard lib.
Re:Why Go and Not Rust? by leshow on Monday September 16, 2019 @12:09PM (#20985524)
literally any data structure that stores different types similarly. hashmaps, linkedlists, vectors, binary trees (assuming you can bound on some ordering function), etc, then all the operations you expect to work on those data structures. You could also define mapping operations that work on the containing structure and don't care much about the specific type of the contents. There's a million and one uses for generics (polymorphism).

In Go, I believe you need to give up type safety to write these implementations, by using Interface{}

Re:Why Go and Not Rust? by rmilejczz on Monday September 16, 2019 @11:20AM (#20984935)
A lot of the time you’ll end up with an implementation for each discrete possibility, but that’s pretty crappy especially if you have more than 3 or 4 discrete types you need logic for (though not always bad, some go libs follow this pattern e.g. sort offers a method for each basic type). You could use the empty interface (which is essentially ‘any’), but that generally requires you to do casting or runtime type checking and that’s suboptimal in a statically typed, compiled language.

Generally it makes more sense to describe an interface that can be satisfied by different types, and writing logic to handle that interface instead. Go may not have traditional generics but the interface pattern is certainly a kind of generic programming that can be used to achieve some powerful results. Because structs can satisfy many interfaces and interfaces can be satisfied with any kind of struct (if you want an in language example check out the io.Writer interface and the many functions that use it) you can end up with highly reusable code despite not having traditional generics.

Re:Why Go and Not Rust? by atoav on Monday September 16, 2019 @12:48PM (#20985962)
In my experience Generics become much more useful, when your typesystem is strict. I use them quite often in Rust, especially when writing functions that should take many different types as inputs/outputs.

You can get away without them in many cases, but if you are working on stuff that you want to reuse, generics are the way to go. In combination with Rust’s traits this is especially cool.

Re:Why Go and Not Rust? by zenhack on Monday September 16, 2019 @05:29PM (#20988748)