Hacker News new | ask | show | jobs
by dilap 4258 days ago
Your 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".
2 comments

> Your examples aren't quite equivalent -- `people` in Go refers to a contiguous chunk in memory of size len(people) x sizeof(People).

How it's laid out in memory is tangential to the original critique - namely, that Go introduces complexity by not having parametric polymorphism.

> But we are doing something you can't do at all in Python (barring specialized array modules or similar).

Python not only allows this (I don't see why it wouldn't), it has built-in support for an equivalent for sorting via rich comparison methods [1]. The point is, though, that you don't have to.

As for the Go example, it's not equivalent because you're writing your own boilerplate code. And it's throwing static typing out the window, which doesn't seem very idiomatic.

1: https://wiki.python.org/moin/HowTo/Sorting#Odd_and_Ends

Hmm? Sorry, I wasn't trying to say Python doesn't let you define comparison functions, I was just reiterating that it doesn't let you sort an array of embedded objects, as opposed to pointers.

This is not a tangential difference. It's a real, important language decision, with benefits and consequences.

If you restrict yourself to arrays of interface{}, like python, then you can do exactly what python does, at the cost of losing compile-time type checking...exactly like python :)

I believe my example is equivalent to the python code; it doesn't contain any boilerplate (which is something you have to write over and over again), but rather a short library function, SortBy, which you would only have to write once.

(The normal Go sorting routines do contain boilerplate, since the Swap and Len methods are basically copy-paste jobs.)

That said, I agree using interface{} everywhere is not idiomatic Go, and certainly using things like map and fold in Go would be so awkward as not to not be worth it.

But doing something like writing a heap class that required casting when removing the object would be pretty reasonable, IMO.

You're not throwing static typing out the window, you're just losing it in that isolated case. It's more like...pushing static typing slightly out of the comfy spot on the couch. That's unfortunate!...but maybe not that bad -- after all, dynamic languages get along alright w/o static checks anywhere. :)

But do those library functions actually exist or did you write them just now?
I 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.)