Why are interface methods not allowed?

17,956 views
Skip to first unread message

ceving

unread,
May 31, 2011, 11:58:21 AM5/31/11
to golang-nuts
I would like to understand the difference between a method and a
function. The following example fails to compile:

package main
import "fmt"

type Any interface{}

func dump (arg Any) {
fmt.Printf ("%#v\n", arg)
}

func (self Any) dump () {
fmt.Printf ("%#v\n", self)
}

func main () {
a := 42
print (a)
a.print()
}

with the error: invalid receiver type Any (Any is an interface type)

What is the reason why a function with an interface argument is
allowed but a method with a interface receiver not?

ceving

unread,
May 31, 2011, 12:04:31 PM5/31/11
to golang-nuts
Sorry this is the correct example:

package main
import "fmt"

type Any interface{}

func dump (arg Any) {
fmt.Printf ("%#v\n", arg)
}

func (self Any) dump () {
fmt.Printf ("%#v\n", self)
}

func main () {
a := 42
dump (a)
a.dump()

Brad Fitzpatrick

unread,
May 31, 2011, 12:11:16 PM5/31/11
to ceving, golang-nuts
Your variable 'a' is an int, not an Any.

Change it to:

var a Any = 42

But even then, you can't define methods on an interface type so a.dump() won't work.  See: http://golang.org/doc/go_spec.html#Method_declarations 

ceving

unread,
May 31, 2011, 12:18:28 PM5/31/11
to golang-nuts
On May 31, 6:11 pm, Brad Fitzpatrick <bradf...@golang.org> wrote:

> But even then, you can't define methods on an interface type so a.dump()
> won't work.  See:http://golang.org/doc/go_spec.html#Method_declarations

Yes I already read that. The question is: Why is it that way?

Kyle Lemons

unread,
May 31, 2011, 12:20:32 PM5/31/11
to Brad Fitzpatrick, ceving, golang-nuts
You can define methods on a struct type with an embedded interface.

package main

import "fmt"

type Any interface{}

type AnyValue struct {
    Any
}

func (av AnyValue) Dump() {
    fmt.Printf("%#v\n", av.Any)
}

func main() {
AnyValue{42}.Dump()
--
~Kyle

"Everyone knows that debugging is twice as hard as writing a program in the first place. So if you're as clever as you can be when you write it, how will you ever debug it?" 
— Brian Kernighan

Marko Mikulicic

unread,
May 31, 2011, 12:32:00 PM5/31/11
to ceving, golang-nuts
Methods are defined on the concrete types implementing an interface. An interface only declares methods. Any type which implements the methods declared in the interface, implicitly implements the interface.

package main
import "fmt"

type Any interface{
  Dump()
}

func Dump (arg Any) {

   fmt.Printf ("%#v\n", arg)
}

func (self int) Dump () {
   fmt.Printf ("My special int dump %#v\n", self)

}

func main () {
   a := 42
   Dump (a)
   a.Dump()
}

In this example we created an interface "Any" which provideds a method "Dump". Now every type which has a method Dump() is acceptable everywhere you ask for an "Any" value, in this case "int" implements "Any".
This allows you to write generic code which can expect some behavior to be exposed by it's arguments.

Now, if you want to write a method whose "receiver" is an interface, basically you are trying to say that an interface implements an interface:

type Something interface {
  Whatever()
}

func (self Any) Whatever () {
   fmt.Printf ("My special impl of Whatever for any value which implements Any %#v\n", self)
}

This won't compile. Even if this construct were allowed, then you could have ambiguity, consider:

func (self int) Whatever() {
  fmt.Printf ("My special impl of Whatever for ints %#v\n", self)
}

func main() {
   a := 42

   a.Whatever()
}

since we defined that "int"s implement Any (by virtue of having implemented "func (self int) Dump()"), there is an ambiguity: which impl of Whatever would be invoked ?

You might argue that the language could choose for example "the most specialized" impl etc, but I think that this would be against one of the core design principles of Go, that is being simple and good enough (and also fast compile time etc).

So, like Kyle answered, you have to wrap your value into something concrete like a struct, so that go can dispatch on it.

I hope that I understood your question, it's always difficult to understand "why" question :-)

Cheers,
Marko

chris dollin

unread,
May 31, 2011, 12:34:10 PM5/31/11
to ceving, golang-nuts

Because interfaces are about specifying what methods concrete things
must have (to satisfy the interface), not about defining methods.

Chris

--
Chris "allusive" Dollin

ceving

unread,
May 31, 2011, 12:34:27 PM5/31/11
to golang-nuts
On May 31, 6:20 pm, Kyle Lemons <kev...@google.com> wrote:
> You can define methods on a struct type with an embedded interface.

Uff. Thanks. That sounds funny.

Kyle Lemons

unread,
May 31, 2011, 12:46:17 PM5/31/11
to ceving, golang-nuts
I probably should've provided spec references.  Type embedding is something that it took me a few tries to grok.

See specifically the discussion following "A field declared with a type but no explicit field name is an anonymous field (colloquially called an embedded field)." and "Fields and methods (§Method declarations) of an anonymous field are promoted to be ordinary fields and methods of the struct (§Selectors).".

One place in the standard library that uses this sort of embedding is the json package, which passes a buffio.Writer around that's been embedded into a local type so that its own helper methods can be defined on it.

Namegduf

unread,
May 31, 2011, 1:21:11 PM5/31/11
to golan...@googlegroups.com

In short, methods on interfaces make programs conceptually more complex,
and don't do anything of use.

Interfaces are, at present, simply a declaration of required operations
that a type must permit for it to meet that interface.

This would change that, and:

1) Make so a method call on an interface is not essentially an
implementation-specific operation on the data in the interface.

2) Make it so there were two places possibly implementing the code for
a method, providing some of the irritations of inheritance.

3) Make it so you can no longer tell what methods a type must have to
be used by a function (assuming the interface taken does not specify
methods not actually needed) by looking at the function alone.

A fair amount of what you can immediately know when looking at an
interface being used right now would go away; you're in effect
operating with one-level inheritance.


For all that, methods can do only one thing that functions can't:
Satisfy an interface.

As interfaces in interfaces are not a very nice idea at all, methods on
interfaces don't provide any benefit over just using functions.


While it can take some getting used to if you're coming from a language
where making everything be a method is idiomatic, there's nothing
wrong with using functions in Go, and it lets Go's interfaces be nice
and simple.

unread,
May 31, 2011, 2:55:25 PM5/31/11
to golang-nuts
I generally agree with what Marko Mikulicic wrote.

There is an additional aspect that comes to my mind: a method defined
on an interface wouldn't be doing anything "magical". Consider the
following two cases ("Any" would be an interface):

type Dumpable interface { dump() }

1. func (self int) dump() { CODE-1 }
2. func (self Any) dump() { CODE-2 }

In case 1, when somebody calls the method "dump" on a value of type
"Dumpable", the type system of the Go language will do something
magical: the program will switch from a context where you do *not*
know the concrete type of the receiver to a context where you *do*
know the concrete type of the receiver. Such as, from "Dumpable" to an
"int". This conversion is type-safe.

In case 2, if somebody would call the method "dump" on a value of type
"Dumpable", there would be no such magic happening here. Within
"CODE-2" you still have no idea what the concrete type of the
parameter "self" is. There is no conversion happening here. You could
achieve the same "nothing magical is happening here at all" effect by
simply declaring a function:

func dump(self Any) { CODE-2 }

And that is exactly what the Go language forces programmers to do. Or
would you want the Go language to introduce a new language feature
even if there is nothing "magical" about it?

Steven

unread,
May 31, 2011, 6:41:35 PM5/31/11
to ⚛, golang-nuts
On Tue, May 31, 2011 at 2:55 PM, ⚛ <0xe2.0x...@gmail.com> wrote:
I generally agree with what Marko Mikulicic wrote.

There is an additional aspect that comes to my mind: a method defined
on an interface wouldn't be doing anything "magical". Consider the
following two cases ("Any" would be an interface):

type Dumpable interface { dump() }

1. func (self int) dump() { CODE-1 }
2. func (self Any) dump() { CODE-2 }

In case 1, when somebody calls the method "dump" on a value of type
"Dumpable", the type system of the Go language will do something
magical: the program will switch from a context where you do *not*
know the concrete type of the receiver to a context where you *do*
know the concrete type of the receiver. Such as, from "Dumpable" to an
"int". This conversion is type-safe.

In case 2, if somebody would call the method "dump" on a value of type
"Dumpable", there would be no such magic happening here. Within
"CODE-2" you still have no idea what the concrete type of the
parameter "self" is. There is no conversion happening here. You could
achieve the same "nothing magical is happening here at all" effect by
simply declaring a function:

func dump(self Any) { CODE-2 }

And that is exactly what the Go language forces programmers to do. Or
would you want the Go language to introduce a new language feature
even if there is nothing "magical" about it?

Here's some magic it could do, though it would have to be designed that way. Say you have:

type Aer interface { A() }
func (a Aer) B() {}

type Ber interface { B() }
func (b Ber) C() {}

type Cer interface { C() }
func (c Cer) D() {}

type I interface {
    Aer
    Ber
    Cer
}

Aer satisfies Ber, which satisfies Cer, so you can use B, C, and D, and all the  user needs to provide is A. Of course, this is a lousy idea, even at a conceptual level because:

1) You can do the same thing with embedding structs and interfaces, you just need to manually initialize each interface.
2) Its not entirely clear what methods you need to provide, just from looking at the definition of I.
3) What happens if your interface is entirely self satisfying?
4) It makes it more difficult to reason about what happens when you assert the type to a different interface, say interface { A(); B(); C() }.

But, it would be magical! I don't think magic is the criterion that should be used to evaluate these things. C++'s copy constructors and ilk are magical, but mean they have to introduce more features, such as rvalue references, just to solve the unintended problems this "magic" introduces.

 

Kyle Lemons

unread,
May 31, 2011, 6:57:18 PM5/31/11
to Steven, ⚛, golang-nuts
type Aer interface { A() }
func (a Aer) B() {}

type Ber interface { B() }
func (b Ber) C() {}

type Cer interface { C() }
func (c Cer) D() {}

type I interface {
    Aer
    Ber
    Cer
}

As you say, tracing through that is a nightmare. Note also that the ONLY thing you get out of I by adding all of that extra boilerplate is D(), and to make matters worse, Aer and Ber both have a B() method, which are not the same method, as they should be in the normal inclusive case of an interface of interfaces.  Just looking at that example makes my head hurt.
 
Aer satisfies Ber, which satisfies Cer, so you can use B, C, and D, and all the  user needs to provide is A. Of course, this is a lousy idea, even at a conceptual level because:

1) You can do the same thing with embedding structs and interfaces, you just need to manually initialize each interface.
I can't even fathom a serious example that would need to use so many interfaces...  The point of interfaces is to make them lightweight and to describe only the functionality you require out of an object.
 
2) Its not entirely clear what methods you need to provide, just from looking at the definition of I.
3) What happens if your interface is entirely self satisfying?
All interfaces are, by definition, self satisfying.  So if you made interface receivers, you could make interfaces that interface your interface and I really don't think we want to go deeper.
 
4) It makes it more difficult to reason about what happens when you assert the type to a different interface, say interface { A(); B(); C() }.
Since we are currently able to type assert to another interface (see io.Copy http://golang.org/src/pkg/io/io.go?s=8380:8443#L256), I don't see what benefit you'd get from having interface receivers and doing type assertions.

Steven

unread,
May 31, 2011, 7:00:55 PM5/31/11
to Kyle Lemons, ⚛, golang-nuts
On Tue, May 31, 2011 at 6:57 PM, Kyle Lemons <kev...@google.com> wrote:
type Aer interface { A() }
func (a Aer) B() {}

type Ber interface { B() }
func (b Ber) C() {}

type Cer interface { C() }
func (c Cer) D() {}

type I interface {
    Aer
    Ber
    Cer
}

As you say, tracing through that is a nightmare. Note also that the ONLY thing you get out of I by adding all of that extra boilerplate is D(), and to make matters worse, Aer and Ber both have a B() method, which are not the same method, as they should be in the normal inclusive case of an interface of interfaces.  Just looking at that example makes my head hurt.
 
Aer satisfies Ber, which satisfies Cer, so you can use B, C, and D, and all the  user needs to provide is A. Of course, this is a lousy idea, even at a conceptual level because:

1) You can do the same thing with embedding structs and interfaces, you just need to manually initialize each interface.
I can't even fathom a serious example that would need to use so many interfaces...  The point of interfaces is to make them lightweight and to describe only the functionality you require out of an object.
 
2) Its not entirely clear what methods you need to provide, just from looking at the definition of I.
3) What happens if your interface is entirely self satisfying?
All interfaces are, by definition, self satisfying.  So if you made interface receivers, you could make interfaces that interface your interface and I really don't think we want to go deeper.
 
4) It makes it more difficult to reason about what happens when you assert the type to a different interface, say interface { A(); B(); C() }.
Since we are currently able to type assert to another interface (see io.Copy http://golang.org/src/pkg/io/io.go?s=8380:8443#L256), I don't see what benefit you'd get from having interface receivers and doing type assertions.

All good reasons why I was saying that its a bad idea :). Just pointing out that magic ain't all its cracked up to be.

Lars Pensjö

unread,
Jun 1, 2011, 5:28:47 PM6/1/11
to golan...@googlegroups.com
So, ceving's original question was what the difference between a method and a function is.

The already obvious answer would be that methods can be used to satisfy an interface. Apart from that, I find many times that I define functions where I hesitate between doing it as a method or a function.

Is there any other difference? Or is the general recommendation that methods should only be used if the type shall satisfy an interface?

One reason that now comes to my mind is name space pollution, and the possibility to use shorter names.

Personally, I also prefer to do "x.Add()" instead of "Add(x)" if x is a pointer to data that is updated.

Nick Terry

unread,
Feb 7, 2014, 6:34:56 PM2/7/14
to golan...@googlegroups.com, matthew....@gmail.com
Just recently I was wondering about the pitfalls were for methods on interfaces.  There are obvious problems, but are they fatal?  Then I found this thread and Matthew's rules seem to present a sane way to do it. To summarize: methods on interfaces would be treated as syntactic sugar for functions with an interface as the first parameter.

As Matthew points out, embedding is where the corner cases start to look ugly.
Another ugly edge case is comparing what embedding a struct means vs embedding an interface. The methods the new type gets are different between the two. To make them the same you have to throw out rule 3. The previous discussion shows that won't work. A second option is to have embedding and interface methods use different method sets. That's pretty much a non-starter.

So it looks like interface methods create complex problems that only have complex answers. A fatal flaw for adoption to Go. Another nail in the coffin is embedding an interface in a struct is nearly as expressive as interface methods.

As an Aside:
Preferring "dot notation" vs "first parameter" has its roots in spoken language. Humans prefer SVO (subject-verb-object), though SOV is common with pronouns. VSO is a very distant third. It easy to see the parallels.
"Jane loves him" jane.loves(him)
"Jane him loves" ... ? (jane,him)loves
"loves Jane him" loves(jane, him)

Because of this I think trying to preserve SVO in a programming language is generally a worth while en devour.


On Wednesday, June 27, 2012 3:21:00 PM UTC-7, matthew....@gmail.com wrote:
So I came looking around with the same question as the OP, and with the same basic thought process as Lars--I hesitate between choosing a method vs a function on an interface with some operations, partially because I'm used to the "dot notation" method passing idiom rather than the "function with the first argument as the receiver idiom". I know that behind the scenes the two are pretty much the same, in C++ and even more so in Go with the ability to choose how to pass the receiver. obj.method() feels like syntactic sugar, so it's a little jarring seeing a schism in the syntax between working with interfaces and working with structs.

I definitely understand the issues that you all have described. I had assumed methods on interfaces would be useful for composing the methods within an interface in a generic way, while still providing the dot notation syntax. This is a poor if familiar example, but I had pictured something like std::vector::push_back(), which could be implemented in terms of other "atomic" methods like size(), capacity(), reserve() and back(), but which modifies the receiver and therefore makes sense as a method called with dot notation.

Of course Go's closest analog to vector doesn't use dot notation for its append (and doesn't technically modify the passed slice... I still get confused by unused errors with this every now and then), not to mention the fact that the actual std::vector::push_back probably does a lot more optimization than a naively composed version could, but that's not always true for a given interface.

I do think the people here are making different assumptions than I had about the limitations of interface methods, namely:
0) That a method satisfies an interface is not the only utility of a method; it also has utility to the developer by showing that something is receiving a call, whereas a function's first argument could be a receiver or could just be the first argument listed. This is largely due to my coming from a C++ background, but even so; dot-notation is syntax, while first-argument-receives is an idiom that is not always true.
1) An interface would not be able to contain a method X and also have a method X declared on it.
2) In case of a struct and an interface it satisfies each having the same method, the method that would actually be called would be dependent on if you're holding the object as its concrete type, or as the interface. Correct me if I'm wrong, but if an interface doesn't hold a given method, you currently can't know the var's concrete type has that method, at least until you dispatch one of the interface's contained methods, after which point you'll have hit a concrete implementation anyway.
3) Methods on interfaces don't work to satisfy other interfaces. This prevents the Aer-Cer issue, but also basically reduces methods on interfaces to syntactic sugar for functions that take an interface, which was my stated goal above.

Assumption 3 probably runs into issues when you look at embedding interfaces in other interfaces though, if you have something like the following:

type Aer interface{A()...}
func (a Aer) CCC(){...}

type Ber interface{B()...}
func (b Ber) DDD(){...}

type I interface{
    Aer
    Ber
}

var ii I
...
ii.CCC()

You're not relying on methods on `Aer` or `Ber` to satisfy I, but you'd probably still expect `ii.CCC()` and `ii.DDD()` to be valid, since `Aer and `Ber each have the methods necessary for the two methods to work. If you changed `DDD()`'s name to also be `CCC()`, you'd the multiple inheritance problem all over again.

Because of that, the simplest solution would be to disallow `I` from dispatching any methods declared on Aer or Ber without casting to one or the other, which if you squint makes sense as an extension of assumptions 2 and 3-- neither `Aer` nor `Ber` contains `CCC()`, and even if `I` did, methods on interfaces don't satisfy other interfaces. This would probably minimize compiler effort, since validating a call to `ii.CCC()` would only involve looking at what methods are contained by `I` and its embedded types, and those directly declared on `I`.

Anyway, I really am attacking this from the perspective of methods on interfaces being almost orthogonal to methods on structs, with the only overlap that they both enable a syntax I subjectively find more clear than functions on interfaces. I think my limitations would allow this to play well with other behaviors of interfaces, but I'm sure I'm missing something, and either way I'm sure the limitations would provide more confusion to new Go devs than having dot notation would remove. Please take this not as a formal proposal, but more as a brain dump of a feature I had expected, and how I expected it to behave. I'd be interested to see whatever holes you guys could poke in it :D

-Matt Abbott
Reply all
Reply to author
Forward
0 new messages