请参阅Golang接口方法中的实现类型

I want to define an interface for any coordinate-type-thing which implements a Distance() method that can be used to calculate distance (maybe cartesian, maybe chronological, whatever) from another coordinate-type-thing of the same type.

This is the general idea:

    type Distancer interface {
        Distance(other interface{}) int
    }

This doesn't provide type-safety for the argument to Distance(), though, which must be of the same type. For example, I might have a ParticularTime struct that implements Distance(); passing another ParticularTime object as the argument makes sense, but passing a ParticularLocation doesn't make sense at all.

I suspect this could be caught at compile-time at least in some cases, e.g. this is obviously wrong:

    x := ParticularLocation{}
    y := ParticularTime{}
    distance := x.Distance(y)

Is there any way to express this restriction? Or do I have to do runtime type-checking inside every implementation of Distance() ?

Am I thinking about this problem the wrong way?

This is an homomorphism of the expression problem. Also relatively easy to generalize, if we assume distance to mean euclidean distance, which is a reasonable assumption for a broad range of applications. As Go does not support dependent types or anything that fancy, though, we will have to make a reasonable compromise in the amount of dimensions that we want to support, in favor of type-safety.

Let's implement this for the first 3 dimensions.

type OneVectorer interface {
    Vector() [1]float64
}

type TwoVectorer interface {
    Vector() [2]float64
}

type ThreeVectorer interface {
    Vector() [3]float64
}

Then three type-safe methods:

func OneDimensionalDistance(a OneVectorer, b OneVectorer) float64 {
    return euclideanDistance(a.Vector()[:], b.Vector()[:])
}

func TwoDimensionalDistance(a TwoVectorer, b TwoVectorer) float64 {
    return euclideanDistance(a.Vector()[:], b.Vector()[:])
}

func ThreeDimensionalDistance(a ThreeVectorer, b ThreeVectorer) float64 {
    return euclideanDistance(a.Vector()[:], b.Vector()[:])
}

func euclideanDistance(a, b []float64) float64 {
    // invariant: a and b have same length
    c := 0.0
    for i, _ := range a {
        c += math.Pow(b[i]-a[i], 2)
    }
    return math.Sqrt(c)
}

So, a point in time can be a OneVectorer, a cartesian point a TwoVectorer, and so on...

You can define further types to your convenience in order to make the program more expressive and in their methods map down to this vector arithmetic.