4 ms·
Come now -- you don't have to re-implement containers for specific type in Go any more than you do in, say, Python -- you just have to cast when you remove obj
by dilap 12y ago
Come now -- you don't have to re-implement containers for specific type in Go any more than you do in, say, Python -- you just have to cast when you remove objects from the container.
Not great, since you're losing compile-time safety for the type -- but no worse than every single line written in a "dynamic" language like Python, and no worse than pre-generics java, say, or obj-c.
- dons 12y ago> no worse than pre-generics java exactly.
- deleted 12y ago[deleted]
- m0th87 12y agoPython's dynamic typing saves you a lot over static typing without parametric polymorphism, as in Go's case. As a concrete example, take sorting. In Go, you have to implement `sort.Interface` for each custom type you define. An example taken from the `sort` package: package main import ( "fmt" "sort" ) type Person struct { Name string Age int } func (p Person) String() string { return fmt.Sprintf("%s: %d", p.Name, p.Age) } // ByAge implements sort.Interface for []Person based on // the Age field. type ByAge []Person func (a ByAge) Len() int { return len(a) } func (a ByAge) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age } func main() { people := []Person{ {"Bob", 31}, {"John", 42}, {"Michael", 17}, {"Jenny", 26}, } fmt.Println(people) sort.Sort(ByAge(people)) fmt.Println(people) } An equivalent in Python is quite a bit simpler: class Person(object): def __init__(self, name, age): self.name = name self.age = age def __repr__(self): return "%s: %s" % (self.name, self.age) people = [ Person("Bob", 31), Person("John", 42), Person("Michael", 17), Person("Jenny", 26), ] print people people.sort(key=lambda person: person.name) print people If it were just sorting, it wouldn't be that big of a deal. But there's a lot of nice abstractions missing from Go due to the lack of generics - e.g. higher-order functions like `map`.
- dilap 12y agoYour examples aren't quite equivalent -- `people` in Go refers to a contiguous chunk in memory of size len(people) x sizeof(People). Thus, in the Sort() method, there needs to be something that binds the sort-algorithms need to swap elements to the specific code to swap that many bytes. Go does this, at the cost of some boilerplate, with the ByAge interface. (And other languages have more elegant ways to do this.) But we are doing something you can't do at all in Python (barring specialized array modules or similar). The equivalent of the Python code would be something like: package main import ( "fmt" "sort" ) type Person struct { Name string Age int } func (p Person) String() string { return fmt.Sprintf("%s: %d", p.Name, p.Age) } func main() { people := []interface{}{ Person{"Bob", 31}, Person{"John", 42}, Person{"Michael", 17}, Person{"Jenny", 26}, } fmt.Println(people) SortBy(people, func(a, b interface{}) bool { return a.(Person).Age < b.(Person).Age }) fmt.Println(people) } where we have a library function // SortBy can sort anything -- just give a comparison function. func SortBy(elts []interface{}, less func(a, b interface{}) bool) { sort.Sort(byFunc{elts, less}) } type byFunc struct { elts []interface{} less func(a, b interface{}) bool } func (a byFunc) Len() int { return len(a.elts) } func (a byFunc) Swap(i, j int) { a.elts[i], a.elts[j] = a.elts[j], a.elts[i] } func (a byFunc) Less(i, j int) bool { return a.less(a.elts[i], a.elts[j]) } This is exactly the same, expect for instead of saying "Give me .age" like we do in Python, we have to explicitly cast and say "I'm expecting a person, give me .Age".
- mike_hearn 12y agoBut do those library functions actually exist or did you write them just now?
- dilap 12y agoI wrote SortBy, but it was trivial, and you'd only have to do it once. (The bigger issue is probably in Go you'd rather just have the boiler-plate than using []interface{}. But that's what you're essentially doing in a dynamic language like Python, and you can do it in Go, too, if you want.)
- pcwalton 12y ago> obj-c Objective-C is essentially dynamically typed.
- dilap 12y agoTruth, but it does let you write down types for method arguments and instance variables, and the compiler will warn(!) you if you violate those -- if you're sane, you'll treat those warnings as errors. So it's got a static typing feel, even if it is all just dynamic method lookup at runtime.
- pdpi 12y ago> and no worse than pre-generics java Personally, that was the turning point when Java became palatable. Which is a shame, because Go is all-around a pretty nice language. But writing real, production code, I've felt that I was needlessly repeating myself because of the lack of generics. Specifically, I really wish I could write generics over channels, so I can solve the problems of multiplexing/demultiplexing channels, throttling them, etc exactly once.
- dilap 12y ago(It's worth pointing out that Go does offer lots that pre-generics java doesn't, in particular interfaces w/ structural typing.) Agreed that not having generics is a real pain-point, though not as bad as I would've assumed before using the language. For the channel stuff, you could always just make use "chan interface{}", have your generic chan library, and then cast when receiving from the channel. Or you could use code generation to make specific versions of your library for various types, maybe using the upcoming go generate. It's kind of interesting, because C++ templates, for example, essentially are code-generation, but just hidden away, and for that reason tend to get overused (IMO), resulting in crazy-long compile times. If Go's solution to generics ends up being "generate the code explicitly", it would seem uniquely Go-ish -- inelegant, but might work well practice. (I do sort of dream about some kind of generics in Go -- something like where X interface{} type LinkedList X struct { Obj X Next *LinkedList X } and maybe... where X Lessable type Heap X []X func (h *Heap X) Push(x X) { ... } or even just where X interface{} func PushHeap(h []X, x X, less func (a,b X) bool) { ... } Ah, to dream :)
- imanaccount247 12y ago>but no worse than every single line written in a "dynamic" language like Python Yes, much worse. >and no worse than pre-generics java That is setting the bar so low it is underground. I don't want "no worse than a language that was born obsolete 20 years ago", I want "good".