Re: [go-nuts] Why not use methods to save some typing? (e.g. foo.Contains(bar) instead of strings.Contains(foo, bar))

883 views
Skip to first unread message

Andrew Gerrand

unread,
Apr 30, 2013, 9:31:12 AM4/30/13
to amedve...@gmail.com, golang-nuts
To keep the language simple.

The design goals are discussed in some detail here:


On 30 April 2013 07:03, <amedve...@gmail.com> wrote:
Hello everyone,

I just started learning Go, and I do realize this is not an OO language, but why not use methods instead of package functions to make things less verbose?

Compare:

foo.Contains(bar)
strings.Contains(foo, bar)

I've already seen a couple of "convenience methods" that repeat functionality of corresponding functions (like Sort() for example). Why not do the same for strings?

-Alex

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

amedve...@gmail.com

unread,
Apr 30, 2013, 12:56:32 PM4/30/13
to golan...@googlegroups.com, amedve...@gmail.com
Hi Andrew,

I don't really see how foo.Contains(bar) is more complicated than strings.Contains(foo, bar). I would actually say the opposite.

The first variant requires less keystrokes and is more readable and, thus, simpler. It can actually be read as a normal English phrase: "foo contains bar".

Aaron France

unread,
Apr 30, 2013, 12:57:56 PM4/30/13
to amedve...@gmail.com, golang-nuts
Hi,

I believe Andrew is talking of their implementation and not of their use.

-Aaron

amedve...@gmail.com

unread,
Apr 30, 2013, 1:02:07 PM4/30/13
to golan...@googlegroups.com, amedve...@gmail.com
Wouldn't it be something like this?

func (s string) Contains(substr string) bool {
  return strings.Contains(s, substr)

Aaron France

unread,
Apr 30, 2013, 1:03:34 PM4/30/13
to amedve...@gmail.com, golan...@googlegroups.com

Except string is a built in type.

Jan Mercl

unread,
Apr 30, 2013, 1:04:14 PM4/30/13
to amedve...@gmail.com, golang-nuts
On Tue, Apr 30, 2013 at 1:03 PM, <amedve...@gmail.com> wrote:
> I just started learning Go, and I do realize this is not an OO language,

What makes you think that Go is not an OO language? Can you attach
code to a specific type? Of course you can.

> but
> why not use methods instead of package functions to make things less
> verbose?
>
> Compare:
>
> foo.Contains(bar)
> strings.Contains(foo, bar)
>
> I've already seen a couple of "convenience methods" that repeat
> functionality of corresponding functions (like Sort() for example). Why not
> do the same for strings?

The simple answer is that methods can be defined, per specs, only for
user defined (aka "named") types. The predeclared types are not user
defined types.

-j

Andrew Gerrand

unread,
Apr 30, 2013, 1:07:46 PM4/30/13
to amedve...@gmail.com, golang-nuts
I said it keeps the *language* simple. A method must be declared in the same package as the type it is associated with. The string type is declared by the language itself, in a "universe" scope. For the built-in string type to have a Contains method, the language would need to specify contains, or the language would need to permit the declaration of methods on types across package boundaries. Both make the language significantly more complex. The current solution pushes that complexity into the strings package.

Jens Alfke

unread,
Apr 30, 2013, 8:32:43 PM4/30/13
to golan...@googlegroups.com, amedve...@gmail.com


On Tuesday, April 30, 2013 10:04:14 AM UTC-7, Jan Mercl wrote:
What makes you think that Go is not an OO language? Can you attach
code to a specific type? Of course you can.

"Attaching code to a type" is not a definition of OOP, it's just one of the more obvious surface features. I think the OP has a point.

I've been using OOP since the mid-'80s (starting with Smalltalk-80) and I wouldn't call Go fully object-oriented. It's in a bit of a gray area because it has most of the core OO concepts, but in my experience they're not easily combined together in ways that let you build OO designs. (In this respect I would compare it to Lua, and to COM.)

For example, polymorphism is provided through interfaces, and extensibility (overriding) through anonymous struct fields. But the extensibility provided through anonymous fields isn't dynamic — in C++ terms it's as though it's nonvirtual — because the receiver of a method has to be a struct not an interface, so calling a method on the receiver goes through the early-binding struct lookup not the late-binding interface lookup. So there is no easy way for a "base class" to define an overridable method and call it on itself such that a "subclass" can override it.

It's entirely possible that I'm wrong here, but I spent a very frustrating week implementing an OO-type design (with only two classes and a common abstract base class!) in Go a few months ago and afterwards regretted it; I felt like the language was fighting with me the whole way and really, really wanted me to implement everything as "flat" procedural code with some interfaces as syntactic sugar.

Anyway, my point remains that it's far from patently obvious that Go is truly an OO language, if an expert programmer with 30 years of OO experience has trouble making it do really basic OOP tricks.

—Jens

PS: I feel like I should add that I've used quite a variety of languages with various flavors of object-ness, from ST-80 to C++, Objective-C, Java, JavaScript, Python, Ruby and a bit of Scala. So I'm not going to say a language isn't OO just because it doesn't feel exactly like Smalltalk.

Rob Pike

unread,
Apr 30, 2013, 8:49:05 PM4/30/13
to Jens Alfke, golan...@googlegroups.com, amedve...@gmail.com
Go does not have methods on basic types because the designers of the
language did not want methods defined for basic types, in part because
of the knock-on effect they might have on interfaces. I believe we are
all still comfortable with that decision. Others may feel differently.

-rob

Jens Alfke

unread,
Apr 30, 2013, 8:58:02 PM4/30/13
to Rob Pike, golan...@googlegroups.com, amedve...@gmail.com

On Apr 30, 2013, at 5:49 PM, Rob Pike <r...@golang.org> wrote:

> Go does not have methods on basic types because the designers of the
> language did not want methods defined for basic types, in part because
> of the knock-on effect they might have on interfaces.

Could you elaborate on the possible effect on interfaces? I'm not figuring it out from that clue. In particular, since you can create a custom type on a built-in type and then add methods to that, there doesn't seem to be any barrier against methods directly on the base types (although they'd have to be built-in, not extensible.)

--Jens

Rob Pike

unread,
May 1, 2013, 11:25:49 AM5/1/13
to Jens Alfke, golan...@googlegroups.com, amedve...@gmail.com
We simply didn't understand what the implications would be; there's
nothing to explain. Go was designed with caution.

In that vein, look at the size of the strings library. Making all that
functionality methods on the basic type would, as Andrew said,
complicate the language. Why complicate the language with such trivial
things when it can be achieved by a library, which is more
maintainable, easier to extend, and more flexible? The language is
much simpler the way things are.

-rob

Jens Alfke

unread,
May 1, 2013, 11:50:02 AM5/1/13
to Rob Pike, golan...@googlegroups.com, amedve...@gmail.com

On May 1, 2013, at 8:25 AM, Rob Pike <r...@golang.org> wrote:

In that vein, look at the size of the strings library. Making all that
functionality methods on the basic type would, as Andrew said,
complicate the language. Why complicate the language with such trivial
things when it can be achieved by a library, which is more
maintainable, easier to extend, and more flexible? The language is
much simpler the way things are.

This may be quibbling, but I wouldn't count methods on 'string' as part of the language, rather as part of a "standard library". They could be implemented by a slightly-transformed version of strings.go where the functions were declared as methods on 'string', probably using some kind of magic package name like '__builtin' to allow the 'string' type be considered part of the package and thus eligible for method definitions.

It's not a big deal, but to me it would improve the symmetry of the language, removing a disparity between the built-in and user-defined types.

—Jens

Rob Pike

unread,
May 1, 2013, 12:20:59 PM5/1/13
to Jens Alfke, golan...@googlegroups.com, amedve...@gmail.com
The language is much simpler the way things are.

-rob

Gustavo Niemeyer

unread,
May 1, 2013, 12:43:42 PM5/1/13
to Jens Alfke, Rob Pike, golan...@googlegroups.com, amedve...@gmail.com
On Wed, May 1, 2013 at 12:50 PM, Jens Alfke <je...@mooseyard.com> wrote:
> probably using some kind of magic package name like '__builtin' to allow
(...)
> It's not a big deal, but to me it would improve the symmetry of the language,
> removing a disparity between the built-in and user-defined types.

These two ideas are in conflict. Part of what makes the current model
feel good is precisely that the strings package is just another
package. Magic improves disparity rather than symmetry.

> Anyway, my point remains that it's far from patently obvious that Go is truly
> an OO language, if an expert programmer with 30 years of OO experience
> has trouble making it do really basic OOP tricks.

There's a famous quote by Alan Kay saying "When I created
object-oriented programming, C++ is not what I had in mind.", which is
in fact partly bogus because he didn't really create object-oriented
programming, but it at least serves as good evidence that what
object-oriented means isn't as clear cut as one might wish for. It's
also a fairly uninteresting discussion as far as good programming
goes.


gustavo @ http://niemeyer.net

Jens Alfke

unread,
May 1, 2013, 1:10:02 PM5/1/13
to Gustavo Niemeyer, Rob Pike, golan...@googlegroups.com, amedve...@gmail.com

On May 1, 2013, at 9:43 AM, Gustavo Niemeyer <gus...@niemeyer.net> wrote:

These two ideas are in conflict. Part of what makes the current model
feel good is precisely that the strings package is just another
package. Magic improves disparity rather than symmetry.

It doesn't feel good to everyone; that's what triggered this thread.

Forgive me for using the term 'magic'. More clearly, what I meant is to declare a package that implicitly contains the 'string' type. (If all user-defined types are part of a package, why not built-in types? Cf. Ruby's "kernel" module.) To obey Go's rule about only being able to declare methods on a receiver type defined in the same package, the string methods would be declared to be in that same package.

There's a famous quote by Alan Kay saying "When I created
object-oriented programming, C++ is not what I had in mind."

There's another story about him that I like, from his time at Apple. A visitor was giving a talk about a new language, saying it was object-oriented. During the Q&A Alan got up and said something like "this isn't an OO language, it doesn't support inheritance". The guy shrugged and said "Well, who knows what 'object-oriented' really means, anyway?" To which Alan replied, "Well, I'm Alan Kay and I invented Smalltalk, so I think I have a pretty good idea."

it at least serves as good evidence that what
object-oriented means isn't as clear cut as one might wish for. It's
also a fairly uninteresting discussion as far as good programming
goes.

Thus my PS pointing out the variety of OO languages I've worked with: I'm not measuring Go against some cookie-cutter definition, rather against my experience using OO design patterns. So far I haven't found Go condusive to OO design because its notion of inheritance is too limited, as I wrote before. (I would love to be proven wrong, if someone has an example of doing inheritance in a clean way…?)

I was initially responding to another poster's cookie-cutter retort that since you can attach methods to structs, Go is obviously object-oriented.

—Jens

Jan Mercl

unread,
May 1, 2013, 1:35:15 PM5/1/13
to Jens Alfke, golang-nuts, Rob 'Commander' Pike, amedve...@gmail.com, Gustavo Niemeyer

Not to structs. To any user defined type, eg. 'type Int int'.

And BTW,  Go has full inheritance (the behavioral one). Still I don't consider inheritance a necessary thing of an OOP language.

-j

Jens Alfke

unread,
May 1, 2013, 3:33:52 PM5/1/13
to Jan Mercl, golang-nuts, Rob 'Commander' Pike, amedve...@gmail.com, Gustavo Niemeyer

On May 1, 2013, at 10:35 AM, Jan Mercl <0xjnml@gmail.com> wrote:

And BTW,  Go has full inheritance (the behavioral one). Still I don't consider inheritance a necessary thing of an OOP language.

As I wrote earlier in this thread, I haven't found Go's forms of inheritance conducive to working in an OO style:

"For example, polymorphism is provided through interfaces, and extensibility (overriding) through anonymous struct fields. But the extensibility provided through anonymous fields isn't dynamic — in C++ terms it's as though it's nonvirtual — because the receiver of a method has to be a struct not an interface, so calling a method on the receiver goes through the early-binding struct lookup not the late-binding interface lookup. So there is no easy way for a "base class" to define an overridable method and call it on itself such that a "subclass" can override it."

Reading through the Wikipedia article, I think what's missing is what Pierce calls "open recursion", i.e. late-binding of calls to the receiver, in his list of fundamental OO features.

And I'm pointing this out not so much to nitpick about whether Go is or isn't OO, but because it really threw a wrench into my attempts to design Go code in an OO style. I should probably post an example of what I did (in a new thread) and then let people suggest how it could be done better.

—Jens

PS: As for the suggestions to just say "type MyString string" and then declare methods on MyString — that would be great except that there's no way to use that type in real code without adding a blizzard of type-casts between string and MyString all over the place.

Jan Mercl

unread,
May 1, 2013, 5:12:03 PM5/1/13
to Jens Alfke, golang-nuts
On Wed, May 1, 2013 at 9:33 PM, Jens Alfke <je...@mooseyard.com> wrote:
> And I'm pointing this out not so much to nitpick about whether Go is or
> isn't OO, but because it really threw a wrench into my attempts to design Go
> code in an OO style.

The wrench throwing entity is not Go. You are fighting the language.
As in writing C in Basic, or writing Java in Perl etc., or SmallTalk
in Go or whatever similar strange possibility you can imagine.

Writing Go in Go is actually the easiest option. Why choose the hard way?

-j

Aaron France

unread,
May 1, 2013, 5:13:12 PM5/1/13
to Jan Mercl, Jens Alfke, golang-nuts
Is thread still alive?


Steven Blenkinsop

unread,
May 1, 2013, 5:14:19 PM5/1/13
to Jens Alfke, Rob Pike, golan...@googlegroups.com, amedve...@gmail.com
On Wed, May 1, 2013 at 11:50 AM, Jens Alfke <je...@mooseyard.com> wrote:


This may be quibbling, but I wouldn't count methods on 'string' as part of the language, rather as part of a "standard library". They could be implemented by a slightly-transformed version of strings.go where the functions were declared as methods on 'string', probably using some kind of magic package name like '__builtin' to allow the 'string' type be considered part of the package and thus eligible for method definitions.

It wouldn't work in Go, but the way Rust deals with this is interesting. Methods are bound to the polymorphic type (trait) and then overloaded for each implementing type. This means that when you create a new trait, you are free to define implementations for types you don't own. Thus, the language defines built-in types and the libraries create traits and implement them for the built-in types without requiring any special privileges. Go, on the other hand, relies on structural typing for polymorphism, which means any methods on a type have to be defined by the owner of the type, and thus built-in types can only have built-in methods. Go's approach is definitely simpler and better understood (just look at all the design work that's gone into making method dispatch based on a (type, trait) pair sane in Rust), and that fits well in Go, but Rust's approach seems workable too and is arguably more powerful.

As for OO style, that's not really meaningful, since the style you're referring to is inheritance-based and obviously won't work in a language that doesn't support inheritance, regardless of how you decide to define OO.

Dougx

unread,
May 1, 2013, 9:33:12 PM5/1/13
to golan...@googlegroups.com
You could quite easily implement this yourself, like this:

package main

import "fmt"
import "strings"

type String string

func (self String) HasPrefix(prefix string) bool {
  return strings.HasPrefix(string(self), prefix)
}

func main() {
  var e String = "XHello World"
  var result = e.HasPrefix("Hello")
  fmt.Printf("Result: %s -> %+v", e, result)

  e = "Hello World"
  result = e.HasPrefix("Hello")
  fmt.Printf("Result: %s -> %+v", e, result)
}

However, the result ends up being awkward because go doesn't allow you to do the equivalent of a local namespace import (aka. pythons 'from blah import XXX') so you'll be forever stuck doing this sort of thing if you do it:

import s "common/string"

...

func (self *myThing) doThing(name s.String, value int) {
    ...
}

I suspect it'd be easier to champion a special kind of namespace-less import and a strings library, if you're really keen on this.

The way to do it would be to create a wrapper that implements all the functions of say, strings, on a new type and put it up for people to use.

If it turns out to be popular, there would be a reasonable case for making the syntax for using it less awkward.

~
Doug.

Kevin Gillette

unread,
May 1, 2013, 9:33:39 PM5/1/13
to golan...@googlegroups.com, Jens Alfke, Rob Pike, amedve...@gmail.com
If Rust does as you say, according to many definitions of "powerful", it is indeed more powerful (in that aspect) than Go. So is Ruby, Python, (probably) Perl, JavaScript, and C++. That said, all of those languages have areas of exceptional complexity compared to Go. The tradeoff in Go's favor is that there are fewer good ways to fulfill the same common needs, so Go is easier to learn, easier to read, and requires less cognitive load for the average programmer to write.

I'm personally ecstatic that the Go designers kept the standard concrete types from sprouting methods -- the language feels much cleaner because of that. IMO, it should no longer be considered the product of cautious design -- the language is better without builtin methods (whether or not you consider those part of the stdlib or part of the language).

Steven Blenkinsop

unread,
May 1, 2013, 9:51:25 PM5/1/13
to Kevin Gillette, golang-nuts, Jens Alfke, Rob Pike, amedve...@gmail.com
Hence why I made sure to emphasize the complexity cost not being the right fit for Go ;). I just wanted to mention how this type of situation has been handled in other languages.


--

Miguel Pignatelli

unread,
May 2, 2013, 6:21:52 AM5/2/13
to Jan Mercl, Jens Alfke, golang-nuts
I have seen this pattern many times in this list:

A - Go is not an OO language
B - Who said that?? You can... <<insert here at least one OO concept>>
... in Go!

Only to say later...

B - Stop trying to write <<insert any classic OO language here>> in Go!

Come on!! Really???
I understand that being precise in the language is important, but I
think we know what the OP means with "Go is not an OO language", I don't
think that nitpicking is constructive in this case and leads to semantic
discussions that go nowhere.

Just my 2c,

M;

Sean Russell

unread,
May 2, 2013, 6:33:16 AM5/2/13
to golan...@googlegroups.com
On Wednesday, May 1, 2013 9:33:12 PM UTC-4, Dougx wrote:
You could quite easily implement this yourself, like this:

package main

import "fmt"
import "strings"

type String string
...

Except for this, which in an earlier post he mentions as:


that would be great except that there's no way to use that type in real code without adding a blizzard of type-casts between string and MyString all over the place.

As I've mentioned before elsewhere, I'd like to better understand exactly what the problem would be with allowing functions to be defined on a struct outside of a struct package.  It would be simple enough to prevent changing behavior by having the compiler enforce the simple rule that you can't override an already-defined function. It would allow result in cleaner-reading code and would provide more type safety (type enforced at the compiler level rather at runtime with casting).  I fail to see how it would complicate the language or make reading code more difficult.

I'm not a compiler guy, so there may be some technical challenge that makes package extension difficult or computationally complex in the compiler; so far I haven't seen any argument other than the "programmers are stupid" one for the rule preventing foreign package type extension.

--- SER

Jesse McNelis

unread,
May 2, 2013, 6:34:02 AM5/2/13
to amedve...@gmail.com, golang-nuts
On Tue, Apr 30, 2013 at 9:03 PM, <amedve...@gmail.com> wrote:
I just started learning Go, and I do realize this is not an OO language, but why not use methods instead of package functions to make things less verbose?

One of the reasons is that because of interfaces the Go linker can't remove unused methods on type that are used, because they might be dynamically used in an interface at some unknown point in program execution.

Thus any functionality that is a method increases the size of the final binary.

It also creates inconsistency. If the language added methods to the standard 'string' type there would need to be great discussion about what methods they should be, anything not included would still have to be functions in a package probably called 'strings' or 'stringutls' etc.
Then there would be the same discussion about including those functions as methods on the standard string type and so on and so on.
Eventually we end up with something like Ruby's standard string type which has 92 methods.

 

 

--
=====================
http://jessta.id.au

Jan Mercl

unread,
May 2, 2013, 7:07:39 AM5/2/13
to Miguel Pignatelli, golang-nuts


On May 2, 2013 12:21 PM, "Miguel Pignatelli" <eme...@gmail.com> wrote:
> I have seen this pattern many times in this list:
>
> A - Go is not an OO language
> B - Who said that?? You can... <<insert here at least one OO concept>> ... in Go!
>
> Only to say later...
>
> B - Stop trying to write <<insert any classic OO language here>> in Go!
>
> Come on!! Really???

Really. What's so strange about the observation of different OOP languages implement different OOP features?

My opinion is that OOP is code attached to a type since Borland came with the 'object' keyword in some version of theirs Pascal. Yeah, that was my first "OOP" experience.

-j

Dave Cheney

unread,
May 2, 2013, 7:14:59 AM5/2/13
to Jan Mercl, Miguel Pignatelli, golang-nuts
Lads, you've successfully beaten any useful information out of this
thread. How about taking a timeout, eh ?

Dougx

unread,
May 2, 2013, 7:59:26 AM5/2/13
to golan...@googlegroups.com
Obviously if you extend string with a String type, you'd call your custom version of the same function, myinstance.Split(), not strings.Split(), but if you did want to call strings.Split() for some reason, it's trivial to add string(myString) in the few interfaces which mandate the use of string.

If you're just waxing philosophical now and not actually interested in a practical solution, my apologies for interjecting. :)

Cheers,
Doug.

Steven Blenkinsop

unread,
May 2, 2013, 11:38:12 AM5/2/13
to Sean Russell, golan...@googlegroups.com
On Thursday, May 2, 2013, Sean Russell wrote:

As I've mentioned before elsewhere, I'd like to better understand exactly what the problem would be with allowing functions to be defined on a struct outside of a struct package.  It would be simple enough to prevent changing behavior by having the compiler enforce the simple rule that you can't override an already-defined function. It would allow result in cleaner-reading code and would provide more type safety (type enforced at the compiler level rather at runtime with casting).  I fail to see how it would complicate the language or make reading code more difficult.

I'm not a compiler guy, so there may be some technical challenge that makes package extension difficult or computationally complex in the compiler; so far I haven't seen any argument other than the "programmers are stupid" one for the rule preventing foreign package type extension.

The problem is coherence. Let's say two different packages define a method Foo on a type T, then someone else needs to import both packages. Which Foo do you use? Which Foos do the two packages see? What about when one of the packages puts a T in an interface and returns it to you? What about when you pass it into the other package? What about when they convert the interface to a different interface type? Frankly, it gets really messy since you're basically creating a global namespace, which defeats the purpose of the package system and creates major scalability issues.

Sean Russell

unread,
May 5, 2013, 10:23:29 PM5/5/13
to golan...@googlegroups.com
Hi Doug,

On Thursday, May 2, 2013 7:59:26 AM UTC-4, Dougx wrote:
...

If you're just waxing philosophical now and not actually interested in a practical solution, my apologies for interjecting. :)

Not at all.  I'm considering the case where you've mixed in or aliased string to String and are passing it around, but are also wanting to (say) pass it in to a bunch of other library functions which take strings but not String.  In all of these cases, you must cast the type. Which comes back to Alexander's statement which I keep quoting.

that would be great except that there's no way to use that type in real code without adding a blizzard of type-casts between string and MyString all over the place.

I'm inferring by the fact that nobody has addressed this directly is that the type casting is preferable to the language and compiler changes that would be necessary to support allowing package-external-type extension.

--- SER

Sean Russell

unread,
May 5, 2013, 10:33:34 PM5/5/13
to golan...@googlegroups.com, Sean Russell
Hi Steven,


On Thursday, May 2, 2013 11:38:12 AM UTC-4, Steven Blenkinsop wrote:
On Thursday, May 2, 2013, Sean Russell wrote:

As I've mentioned before elsewhere, I'd like to better understand exactly what the problem would be with allowing functions to be
 ...
The problem is coherence. Let's say two different packages define a method Foo on a type T, then someone else needs to import both packages. Which Foo do you use? Which Foos do the two packages see? What about when one of the packages puts a T in an interface and returns it to you? What about when you pass it into the other package? What about when they convert the interface to a different interface type? Frankly, it gets really messy since you're basically creating a global namespace, which defeats the purpose of the package system and creates major scalability issues.

These are all good questions. The current rule is that no functions on types are allowed to be defined outside of the package in which the type is defined, yes? What if the rule were that only package-local functions were allowed to be defined?  None of the questions you raise above would be relevant any longer, would they? foo() called from the package is always the foo() defined in that package.

I don't think it's unusual for somebody to want to add utility functions to a type, for use only within their code, and only visible to their code.  On the other hand, I also don't think it's horrible to just define the functions and have them take the object as the first argument, which is (essentially) what's happening behind the scenes anyway, and that's how I usually get around this issue.  It's less clean, but I can understand if it's a hassle to support in the compiler.

Anyhoo, of all of the wacky ideas that come up in this list, this is one language rule that surprised me most when I was first learning Go, and it's stuck with me.  It doesn't shock me when it surprises other people.

--- SER

Ian Lance Taylor

unread,
May 5, 2013, 10:37:32 PM5/5/13
to Sean Russell, golan...@googlegroups.com
On Sun, May 5, 2013 at 7:33 PM, Sean Russell <seaner...@gmail.com> wrote:
>
> These are all good questions. The current rule is that no functions on types
> are allowed to be defined outside of the package in which the type is
> defined, yes? What if the rule were that only package-local functions were
> allowed to be defined? None of the questions you raise above would be
> relevant any longer, would they? foo() called from the package is always the
> foo() defined in that package.

It's not that simple. What if I pass the object to a function from
some other package, a function that takes a value of some interface
type? What if multiple packages define a method String on the type T,
and one of those packages, or some other package, passes a value of
type T to fmt.Print? Which String method should be called? What if
package A defines String, passes a value of type T to package B, and
package B also defines String, and package B passes the value to
package C, and package C calls fmt.Print? Which String should be
called?

Ian

Steven Blenkinsop

unread,
May 5, 2013, 11:50:02 PM5/5/13
to Ian Lance Taylor, Sean Russell, golan...@googlegroups.com
He's talking about unexported methods. It's already possible for a type to have multipe unexported methods with the same name, each visible only in the package that defines the method and in assignments to interfaces defined in that package (via embedding). Aside from local convenience, a possible application of this would be to put an unexported upcast method from a foreign type T to a type S struct{T} that implements some interface. Then you can provide an exported function converting any type that has such a method to a value of the interface type, rather than requiring the user to know the static type of the value they want to upcast or using a type switch.

Chris Hines

unread,
May 6, 2013, 9:58:47 AM5/6/13
to golan...@googlegroups.com, Ian Lance Taylor, Sean Russell
On Sunday, May 5, 2013 11:50:02 PM UTC-4, Steven Blenkinsop wrote:
He's talking about unexported methods. It's already possible for a type to have multipe unexported methods with the same name, each visible only in the package that defines the method and in assignments to interfaces defined in that package (via embedding).

That's not my understanding of the language spec. The Method Set section[1] says: "In a method set, each method must have a unique method name."

And the Method Declarations section[2] says: "The type denoted by T ... must be declared in the same package as the method.... For a base type, the non-blank names of methods bound to it must be unique. If the base type is a struct type, the non-blank method and field names must be distinct."

And the Selectors section[3] says: "For a value x of type T or *T where T is not an interface type, x.f denotes the field or method at the shallowest depth in T where there is such an f. If there is not exactly one f with shallowest depth, the selector expression is illegal."

The net effect of these rules seems to indicate that for any given concrete type and method name there can be only one callable method with that name and these rules seem to apply equally to exported and unexported methods.

mortdeus

unread,
May 6, 2013, 10:22:09 AM5/6/13
to golan...@googlegroups.com, Ian Lance Taylor, Sean Russell


On Sunday, May 5, 2013 10:50:02 PM UTC-5, Steven Blenkinsop wrote:
On Sunday, May 5, 2013, Ian Lance Taylor wrote:
On Sun, May 5, 2013 at 7:33 PM, Sean Russell <seaner...@gmail.com> wrote:
>
> These are all good questions. The current rule is that no functions on types
> are allowed to be defined outside of the package in which the type is
> defined, yes? What if the rule were that only package-local functions were
> allowed to be defined?  None of the questions you raise above would be
> relevant any longer, would they? foo() called from the package is always the
> foo() defined in that package.

It's not that simple.  What if I pass the object to a function from
some other package, a function that takes a value of some interface
type?  What if multiple packages define a method String on the type T,
and one of those packages, or some other package, passes a value of
type T to fmt.Print?  Which String method should be called?  What if
package A defines String, passes a value of type T to package B, and
package B also defines String, and package B passes the value to
package C, and package C calls fmt.Print?  Which String should be
called?

Ian


He's talking about unexported methods. It's already possible for a type to have multipe unexported methods with the same name, each visible only in the package that defines the method and in assignments to interfaces defined in that package (via embedding). 
What are you talking about? Each type is unique to its own package. If type Bar exists in packages foo1 and foo2, and both packages define a method on Bar called Baz() that doesnt mean they are the same types when imported to your pkg. One type is foo1.Bar and the other type is foo2.Bar. 
 

Steven Blenkinsop

unread,
May 6, 2013, 11:16:30 AM5/6/13
to mortdeus, golan...@googlegroups.com, Ian Lance Taylor, Sean Russell
On Monday, May 6, 2013, Chris Hines wrote:
On Sunday, May 5, 2013 11:50:02 PM UTC-4, Steven Blenkinsop wrote:
He's talking about unexported methods. It's already possible for a type to have multipe unexported methods with the same name, each visible only in the package that defines the method and in assignments to interfaces defined in that package (via embedding).

That's not my understanding of the language spec. The Method Set section[1] says: "In a method set, each method must have a unique method name."

"Unique" in that sentence is a link, which leads to:

> Given a set of identifiers, an identifier is called unique if it is different from every other in the set. Two identifiers are different if they are spelled differently, or if they appear in different packages and are not exported. Otherwise, they are the same.

Unexported identifiers from different packages are always different, even if they're spelled the same, so it's possible for a single type to have multiple methods with the same name as long as they're unexported and defined in separate packages. This situation can arise if you make a `type S struct { pkg.T }`, where `pkg.T` has an unexported method `foo`, and then define your own `foo` method on it. If `pkg.Interface` requires a `foo` method, it won't see yours since it technically has a different name (since the interface method and your method are unexported and declared in separate packages), but will instead see the one defined in `pkg`.

On Monday, May 6, 2013, mortdeus wrote:

What are you talking about? Each type is unique to its own package. If type Bar exists in packages foo1 and foo2, and both packages define a method on Bar called Baz() that doesnt mean they are the same types when imported to your pkg. One type is foo1.Bar and the other type is foo2.Bar. 

If `pkg1.Bar` has an anonymously field with type `pkg2.Baz`, then the methods on `pkg2.Baz` are promoted to be methods on `pkg1.Bar`. Methods at the same depth conflict, and methods at a shallower depth shadow methods at a greater depth, where depth is the number of anonymous fields you have to traverse to get to the one the method was originally defined on. Since unexported methods from different packages are considered to have different names even if they're spelled the same, they don't shadow or conflict with each other, so it's possible to have two `foo` methods on the same type, each from different packages. This isn't a problem, since each one is only visible in the package that defined it.

Chris Hines

unread,
May 6, 2013, 11:41:24 AM5/6/13
to golan...@googlegroups.com, mortdeus, Ian Lance Taylor, Sean Russell
Are you sure that unexported methods are promoted across package boundaries? Wouldn't the method set of the embedding type only contain the exported methods of types it embeds from other packages?

Steven Blenkinsop

unread,
May 6, 2013, 11:53:52 AM5/6/13
to Chris Hines, golan...@googlegroups.com, mortdeus, Ian Lance Taylor, Sean Russell
On Monday, May 6, 2013, Chris Hines wrote:
Are you sure that unexported methods are promoted across package boundaries? Wouldn't the method set of the embedding type only contain the exported methods of types it embeds from other packages?

Yes.  All methods are promoted. You can try it out if you like. Define an interface with an unexported method in one package along with a type that implements it. In another package, create a another type embedding the first type, and assign it to a variable of the interface type.

Chris Hines

unread,
May 6, 2013, 12:46:27 PM5/6/13
to golan...@googlegroups.com, Chris Hines, mortdeus, Ian Lance Taylor, Sean Russell
Very interesting. I hadn't factored in the interaction with an exported interface like that. Thanks for the education.
Reply all
Reply to author
Forward
0 new messages