stephan
stephan

Reputation: 42

How can interfaces be comparable and at the same time functions not?

I have the following code-snippet:

type F func()

type I interface {}

func A() {}

func B() {}

func test() {
    var a interface{} = A
    var b interface{} = B
    if A == B { // 1. Compile error 
        // Code
    }

    if a == b { // 2. No compile error
        // Code
    }
}

If functions are not comparable and interfaces are comparable, why can I assign a function to an interface type?

--

To clarify my question another code-snippet:

type I interface {
    DoSomething()
}

type F func()

func (f F) DoSomething() {
    f()
}

func A() {
    fmt.Println("A")
}

func B() {
    fmt.Println("B")
}

func test() {
    var _a F = A
    var _b F = B
    var a I = _a
    var b I = _b

    if a == b { // 2. No compile error but panic
        // Code
    }
}

It seems to me, that I can break the type system with simple assignments.

I do not propose that functions should be comparable. My questions are:

Upvotes: 0

Views: 138

Answers (2)

kostix
kostix

Reputation: 55443

To cite Ian Lance Taylor's message from this thread on the Go mailing list:

On Wed, Nov 23, 2016 at 7:00 AM, T L wrote:

On Wednesday, November 23, 2016 at 10:35:59 PM UTC+8, Axel Wagner wrote:

So, your suggestion is, to have functions be comparable, but have the comparisons always be false (unless compared to nil)? How would that be useful and not completely confusing? e.g. how would that not lead to people asking here, once a week, why (os.Open == os.Open) == false or something like that?

No, I don't os.Open != os.Open, they are the same question, so they are equal.

Even this seemingly simple statement is unclear. Go now supports -buildmode=shared and -linkshared, meaning that a Go program can link against a set of shared libraries that are themselves written in Go. When running in this mode, a function like os.Open can easily appear multiple times in a single program image, in different shared libraries. So while os.Open == os.Open might reasonably always be true, given

func F() func(string) (*os.File, error) { 
    return os.Open 
} 

then it is much less clear whether

F() == os.Open 

should be true, as F might be in a shared library and might return a pointer to a different instance of os.Open.

And that's just one of the reasons. See another one—regarding the function values which are closures with the same code but closed over different variables— explained in that same thread by Jesper Louis Andersen.

I would add that the whole thread is worth thorough reading and absorbing.

Upvotes: 2

user142162
user142162

Reputation:

This is simply how the language is defined. From the spec:

Interface values are comparable. Two interface values are equal if they have identical dynamic types and equal dynamic values or if both have value nil.

A comparison of two interface values with identical dynamic types causes a run-time panic if values of that type are not comparable.

function values are not comparable. However, as a special case, a [...] function value may be compared to the predeclared identifier nil.

That explains why the first if statement in your example fails at compile time, and why the second one fails at runtime.

Upvotes: 5

Related Questions