The lack of expressiveness in Go

(written by lawrence krubner, however indented passages are often quotes). You can contact lawrence at: lawrence@krubner.com, or follow me on Twitter.

This is the strongest attack I’ve ever read about Go:

Deduping elements of a slice happens the following way in go:

package main

import “fmt”

func main() {
// Given the following list:
xs := []int{8, 6, 8, 2, 4, 4, 5, 9}

// For loop is the only generic way to traverse slices, you we have to write the following:
index := map[int]struct{}{}
for _, x := range xs {
index[x] = struct{}{}
}

// We can “easily” acquire the deduped slice by using a for loop again…
deduped := []int{}
for k, _ := range index {
deduped = append(deduped, k)
}
// Hooray, we can use the ‘deduped’ slice!
fmt.Println(deduped)
}
(playground link)

For those who are not familiar with the concept of generics, here is a though experiment: let’s refactor that bit of code by moving it out to a function:

package main

func deduper(xs []int) []int {
index := map[int]struct{}{}
for _, x := range xs {
index[x] = struct{}{}
}
deduped := []int{}
for k, _ := range index {
deduped = append(deduped, k)
}
return deduped
}
Uh-oh: now our method only works on int slices – our for loops would be still generic, but the function definition forces us to tell the type of the input argument. It is an int slice. If somehow we could tell the compiler that we don’t care what kind of slice it is!

You may ask – what if we use the interface{} interface type? It is a bit ugly, but it works! Let’s try that!

func deduper(xs []interface{}) []interface{} {
index := map[int
Uh-oh again. We even had to stop typing. We can not use the empty interface as our key in the map… To be able to use something as a map key the members of that type must be comparable (http://golang.org/ref/spec#Comparison_operators). Empty interfaces are not comparable, since they can represent non-comparable types! This way we can forget our neat implementation which reuses the idempotent nature of setting keys of a map!

Let’s look for an other approach – surely the Go authors have paved the way for us. Let’s take a look at the sort package. We see a quite descriptively named sort.Interface type there:

type Interface interface {
// Len is the number of elements in the collection.
Len() int
// Less reports whether the element with
// index i should sort before the element with index j.
Less(i, j int) bool
// Swap swaps the elements with indexes i and j.
Swap(i, j int)
}
This would be all good, but no methods can be defined on builtin types! Don’t worry! The IntSlice type comes for the rescue! We only have to typecast our []int into an IntSlice and we can use all the functions written by other very smart people. But let’s go back to the deduping function. Let’s try to use the sort.Interface to write our own deduping function. After all, we can compare elements of a slice with it.

Post external references

  1. 1
    http://crufter.com/2014/12/01/everyday-hassles-in-go/
Source