4 ms·
Go's lack of generics and metaprogramming can be extremely frustrating when you're working with services that copy data around from one struct to another, which
by strken 6y ago
Go's lack of generics and metaprogramming can be extremely frustrating when you're working with services that copy data around from one struct to another, which ends up being a lot of them. You're left with reflection, code generation, or doing it field by field.
It'd be nice to have syntactic sugar for splatting structs together, or a library that let you do it in a typesafe and efficient way.
EDIT: I will defend the random map order, though. Golang maps were only predictable up until a set number of items, which caused hard-to-detect bugs where your small test case would be in order but your large real-life data would be out of order.
- hombre_fatal 6y agoI certainly agree. I would love for Go to get to a place where I never have to copy and paste code again. Generics will get us part way there. The next item on my wishlist are algebraic data types. In complex projects I've opted for Rust over Go just because of the abstraction power of being able to represent things like: Dest = Register (A | B | C) Src = Register (A | B | C) | Immediate value OpCode = Nop | Add(Dest, Src) | ... In Go, I'm so tired of `interface{}`. Edit: To respond to your edit about map iter order, that's related to my complaint of Go's lack of dev vs release build. Randomizing map iter order in dev builds is acceptable to me. Go makes you pay for that in prod.
- yuribro 6y agoI don't understand the jump from the lack of algebraic types, to using interface{}. You can spell out all your types into explicit structs (where you can choose between a simple implementation with a little worse performance or to make the implementation a little more complex) and pass them around. There are many complex code bases (kernels & databases come to mind) which use C (not C++), and they don't resort to passing void* around the "business logic" parts. The idea that for a complex project you would choose a different language with so many different characteristics due to a minor detail about the type system (this is not exactly Haskell vs JS...). This kind of decision making would not pass in any reasonable organization...
- kaoD 6y agoI don't know Go. How can you emulate sum types on it? And how do you pattern match them?
- simiones 6y agoIt depends on what you mean exactly by "emulating sum types" and "pattern matching them". An example is rolling your own discriminated union: type Avariant struct { a int } type Bvariant struct { b string } type SumChoice int const ( SumA SumChoice = 0 SumB SumChoice = 1 ) type Sum struct { type SumChoice A *Avariant B *Bvariant } sumA := Sum{choice: SumA, A: &{a: 7} } sumB := Sum{choice: SumB, B: &{b: "abc"} } func foo (x Sum) { switch(x.choice) { case SumA: { fmt.Printf("Value is %d", x.A.a) } case SumB: { fmt.Printf("Value is \"%s\"", x.B.b) } } } As usual with Go, it's ugly and verbose and error prone, but it just about works. The previous poster was probably thinking of something similar but using interface{} (equivalent to Object or void*) instead of Sum, and then pattern matching using an explicit type switch: func foo (x interface{}) { switch actual := x.(type) { case AVariant: { fmt.Printf("Value is %d", actual.a) } case BVariant: { fmt.Printf("Value is \"%s\"", actual.b) } } } This is slightly less risky and has less ceremony, but it's also less clear which types foo() supports based on its signature. Since Go has only structural subtyping [0] for interfaces, you would have to add more boilerplate to use a marker interface instead. [0] In Go, a type implements an interface if it has functions that match (name and signature) the interface functions. The name of the interface is actually irrelevant: any two interfaces with the same functions are equal. So if you declare an interface MyInterface with no methods, it is perfectly equivalent to the built-in `interface{}`: any type implements this interface, and any function which takes a MyInterface value can be called with any type in the program.
- BenFrantzDale 6y agoBut are those two variants held in memory inside Sum or are they heap-allocated sitting out on some other cache line? Can one write a performant Sum type?