Type System Not Uniform

101 views
Skip to first unread message

Quenio

unread,
Nov 11, 2009, 10:18:21 PM11/11/09
to golang-nuts
I just tried to create a method for type "string" and got an error
message saying non-local types.

I think this is a big limitation of the language. you should be able
to extend existing types at least in the local scope.

Scala allows that with implicit conversions and it works really well.

I like to do something like this:

bytearray[0:10].trim()

Instead of having to have to do something verbose like this:

strings.TrimSpace(string(bytearray[0:10]))

By the way, it would be nice if you could define any operator as a
method for any types, too.

- Quenio

Ostsol

unread,
Nov 11, 2009, 11:02:06 PM11/11/09
to golang-nuts
How's this:

package main

import "strings"
import "fmt"

type ByteArray []byte

func (self ByteArray) Trim () ByteArray
{
return strings.Bytes (strings.TrimSpace (string (self)));
}

func main ()
{
value := ByteArray{' ', ' ', 'a', 'b', 'c', ' ', ' '};

value = value[0:4].Trim ();

for _, char := range value
{
fmt.Printf ("%c", char);
}

fmt.Printf ("\n");
}

Go requires explicit conversions and that's probably where the
difficulty is.

-Ostsol

Quenio

unread,
Nov 12, 2009, 10:18:42 PM11/12/09
to golang-nuts
It is not bad, but it is not quite what I'd like to have. I would like
to be able to extend the string type with new methods. Go provides
something really innovative, which is decoupling methods from its
types. It is really a shame you cannot apply that to the pre-defined
types.

On Nov 11, 10:02 pm, Ostsol <ost...@gmail.com> wrote:
> How's this:
>
> package main
>
> import "strings"
> import "fmt"
>
> typeByteArray []byte
>
> func (self ByteArray) Trim () ByteArray
> {
>     return strings.Bytes (strings.TrimSpace (string (self)));
>
> }
>
> func main ()
> {
>     value := ByteArray{' ', ' ', 'a', 'b', 'c', ' ', ' '};
>
>     value = value[0:4].Trim ();
>
>     for _, char := range value
>     {
>         fmt.Printf ("%c", char);
>     }
>
>     fmt.Printf ("\n");
>
> }
>
> Go requires explicit conversions and that's probably where the
> difficulty is.
>
> -Ostsol
>
> On Nov 11, 8:18 pm, Quenio <queniodossan...@gmail.com> wrote:
>
>
>
> > I just tried to create a method fortype"string" and got an error

Adam Langley

unread,
Nov 12, 2009, 10:23:09 PM11/12/09
to Quenio, golang-nuts
On Thu, Nov 12, 2009 at 7:18 PM, Quenio <queniod...@gmail.com> wrote:
> It is not bad, but it is not quite what I'd like to have. I would like
> to be able to extend the string type with new methods. Go provides
> something really innovative, which is decoupling methods from its
> types. It is really a shame you cannot apply that to the pre-defined
> types.

You can, of course, do:

type myString string

func (s myString) Something() {
}


AGL

Quenio

unread,
Nov 12, 2009, 11:55:36 PM11/12/09
to golang-nuts
Of course, but still not the same.

Should I use my own string type in all my API's?

It would be unfamiliar to new API users to see a type that just so
happens to be a string.

I shouldn't have to use a custom type in my API just so that I have
some convenience in my API's implementation.

A custom type should only be used if it will make the API more useful,
clear to its users.

Also, if every API provider decides to have its own custom version of
string, how much more confusing will it be for users of those APIs.

On Nov 12, 9:23 pm, Adam Langley <a...@golang.org> wrote:

Ian Lance Taylor

unread,
Nov 13, 2009, 12:09:34 AM11/13/09
to Quenio, golang-nuts
Quenio <queniod...@gmail.com> writes:

> Of course, but still not the same.
>
> Should I use my own string type in all my API's?
>
> It would be unfamiliar to new API users to see a type that just so
> happens to be a string.
>
> I shouldn't have to use a custom type in my API just so that I have
> some convenience in my API's implementation.
>
> A custom type should only be used if it will make the API more useful,
> clear to its users.
>
> Also, if every API provider decides to have its own custom version of
> string, how much more confusing will it be for users of those APIs.

The problem with adding methods to standard types like string is that
it becomes unclear which interfaces are satisfied by the type. The
set of interfaces would change depending upon which packages you
imported. That seems so potentially confusing that we decided to
require that methods only be permitted on types defined within the
package.

If you want to add methods to a public type, it seems perfectly
reasonable to me that you should give it a new name. If you want to
add methods to a private type, then you can give it a private name,
and use string in the public functions.

Ian

Russ Cox

unread,
Nov 13, 2009, 12:18:03 AM11/13/09
to Quenio, golang-nuts
> Should I use my own string type in all my API's?

No.

> It would be unfamiliar to new API users to see a type that just so
> happens to be a string.
>
> I shouldn't have to use a custom type in my API just so that I have
> some convenience in my API's implementation.
>
> A custom type should only be used if it will make the API more useful,
> clear to its users.
>
> Also, if every API provider decides to have its own custom version of
> string, how much more confusing will it be for users of those APIs.

These are all good reasons for the answer to be no.

Getting back to your original question, about being able to define
methods for other package's types, the fundamental problem is that
this leads to confusion during dynamic interface checks.
For example, suppose you like seeing ints print in hexadecimal,
so you define a method func (i int) String() string that returns
a hexadecimal string. I prefer octal, so I define my own
func (i int) String() string that returns an octal string. Within
our own packages, maybe that makes sense. But what happens
when one of the packages passes an int to fmt.Print? Print will
check if s, ok := i.(Stringer) and print s.String() if ok is true.
Does the int i satisfy Stringer? Which String method does it have?
Does it depend on which package called Print? What if the call to
Print is wrapped by package log? Do you have to continue up
the call stack indefinitely to decide? Things get complicated quickly.

I don't know enough about Scala to say why Scala's implicit
conversions don't have this problem, but one possibility is that
they are done statically by the compiler, so there is no dynamic
runtime aspect.

Russ

Brian Slesinsky

unread,
Nov 13, 2009, 1:43:43 AM11/13/09
to golang-nuts


On Nov 12, 9:18 pm, Russ Cox <r...@golang.org> wrote:
>
> I don't know enough about Scala to say why Scala's implicit
> conversions don't have this problem, but one possibility is that
> they are done statically by the compiler, so there is no dynamic
> runtime aspect.

It seems like most of the problems can be avoided by making it purely
static syntactic sugar so that it doesn't interact with interfaces as
all.

C# has a similar feature:
http://msdn.microsoft.com/en-us/library/bb383977.aspx

There have been some proposals to allow something similar in Java:
http://www.oreillynet.com/onjava/blog/2007/12/extension_methods_proposals.html

Some examples of why you might want this:

Foo.foo(a, b, c) can be written as a.foo(b, c)
Foo.foo(Bar.bar(Baz.baz(a))) can be written as a.baz().bar().foo().

In Go, it would allow you to leave out module names, write pipelines
of functions from left to right instead of inside-out, and potentially
make autocomplete more useful in IDE's. On the other hand, it's harder
to tell which package each function comes from when you're not using
an IDE.

Quenio

unread,
Nov 13, 2009, 11:03:59 AM11/13/09
to golang-nuts
That's interesting. I didn't know that method definitions were bound
to types in the same package. That seems to be very limiting to me. I
think the decoupling of methods and types loses some of its
extensibility potential if you are bound to a package. It is like the
package scope became the new class scope.

On Nov 12, 11:09 pm, Ian Lance Taylor <i...@google.com> wrote:
> Quenio <queniodossan...@gmail.com> writes:
> > Of course, but still not the same.
>
> > Should I use my own stringtypein all my API's?
>
> > It would be unfamiliar to new API users to see atypethat just so
> > happens to be a string.
>
> > I shouldn't have to use a customtypein my API just so that I have
> > some convenience in my API's implementation.
>
> > A customtypeshould only be used if it will make the API more useful,
> > clear to its users.
>
> > Also, if every API provider decides to have its own custom version of
> > string, how much more confusing will it be for users of those APIs.
>
> The problem with adding methods to standard types like string is that
> it becomes unclear which interfaces are satisfied by thetype.  The
> set of interfaces would change depending upon which packages you
> imported.  That seems so potentially confusing that we decided to
> require that methods only be permitted on types defined within the
> package.
>
> If you want to add methods to a publictype, it seems perfectly
> reasonable to me that you should give it a new name.  If you want to
> add methods to a privatetype, then you can give it a private name,

Quenio

unread,
Nov 13, 2009, 11:13:31 AM11/13/09
to golang-nuts
You are right about Scala. Implicit conversions are resolved by the
compiler, and they are bound to the scope where they were defined. To
make it very clear: the source type they are converting may have been
defined in any other scope, but the conversion will only apply in the
scope it was defined.

But I can see now the problem that would arise from allowing
definitions out of the package. Maybe, you could apply the same rules
as in Scala, but those methods defined out of the package scope would
not be available to interfaces; only to the compiler's resolution.

On Nov 12, 11:18 pm, Russ Cox <r...@golang.org> wrote:
> > Should I use my own stringtypein all my API's?
>
> No.
>
> > It would be unfamiliar to new API users to see atypethat just so
> > happens to be a string.
>
> > I shouldn't have to use a customtypein my API just so that I have
> > some convenience in my API's implementation.
>
> > A customtypeshould only be used if it will make the API more useful,

Keith Sheppard

unread,
Nov 13, 2009, 7:35:08 PM11/13/09
to r...@golang.org, Quenio, golang-nuts
Maybe I shouldn't be chiming in since I still know very little about
go but here I go anyway...

The way that interfaces work in go seems very similar to how classes
work in Haskell. Haskell allows class instances (the wording "class
instances" might be misleading in an OO context... think interface
implementation instead) to be defined in any module and it hasn't led
to any serious confusion or other problems that I am aware of. In my
opinion it is a very important factor in the extensibility of Haskell.
For instance I'm not sure how the heavily used "Binary" package
(http://hackage.haskell.org/package/binary) would even be possible
without this since in relies on being able to declare Binary class
instances for all the most commonly used types (Int, Char etc). Are
there different implications for allowing this kind of thing in go (or
is my comparison off base)?

http://www.haskell.org/tutorial/classes.html

Best Regards
Keith
--
keithsheppard.name

Marcin 'Qrczak' Kowalczyk

unread,
Nov 14, 2009, 3:41:43 AM11/14/09
to Ian Lance Taylor, Quenio, golang-nuts
2009/11/13 Ian Lance Taylor <ia...@google.com>:

> The problem with adding methods to standard types like string is that
> it becomes unclear which interfaces are satisfied by the type.

How is it different from adding methods to any other type?

--
Marcin Kowalczyk

Marcin 'Qrczak' Kowalczyk

unread,
Nov 14, 2009, 3:43:14 AM11/14/09
to Ian Lance Taylor, Quenio, golang-nuts
2009/11/14 Marcin 'Qrczak' Kowalczyk <qrcz...@gmail.com>:
I'm sorry, I should have read more carefully. Please disregard this message.

--
Marcin Kowalczyk
qrc...@knm.org.pl

Ian Lance Taylor

unread,
Nov 14, 2009, 11:54:05 AM11/14/09
to Keith Sheppard, r...@golang.org, Quenio, golang-nuts
Keith Sheppard <keit...@gmail.com> writes:

> The way that interfaces work in go seems very similar to how classes
> work in Haskell. Haskell allows class instances (the wording "class
> instances" might be misleading in an OO context... think interface
> implementation instead) to be defined in any module and it hasn't led
> to any serious confusion or other problems that I am aware of. In my
> opinion it is a very important factor in the extensibility of Haskell.
> For instance I'm not sure how the heavily used "Binary" package
> (http://hackage.haskell.org/package/binary) would even be possible
> without this since in relies on being able to declare Binary class
> instances for all the most commonly used types (Int, Char etc). Are
> there different implications for allowing this kind of thing in go (or
> is my comparison off base)?

I think Russ's description below explains the difficulties in Go.

Go does allow interface implementations to be defined in any package.
What it doesn't allow is for one package to define a method for a type
defined in another package.

In Go I think the rough equivalent to Haskell's Binary package would
be Go's encoding/binary package, which simply defines methods which
accept all of the basic types.

Ian

Ola Fosheim Grøstad

unread,
Nov 14, 2009, 1:16:13 PM11/14/09
to golang-nuts
> I don't know enough about Scala to say why Scala's implicit
> conversions don't have this problem, but one possibility is that
> they are done statically by the compiler, so there is no dynamic
> runtime aspect.

Beta allows you to import new methods to classes from files,
statically (Beta doesn't distignuish between functions, methods and
classes, but that doesn't matter here). I found that very useful for
writing more readable code and overcoming shortcomings in libraries
written by others.

(Of course, if you allowed overloading...*nudge*)

Ola.

Sebastian Sylvan

unread,
Nov 14, 2009, 1:38:28 PM11/14/09
to Ian Lance Taylor, Keith Sheppard, r...@golang.org, Quenio, golang-nuts
On Sat, Nov 14, 2009 at 4:54 PM, Ian Lance Taylor <ia...@google.com> wrote:
Keith Sheppard <keit...@gmail.com> writes:

> The way that interfaces work in go seems very similar to how classes
> work in Haskell. Haskell allows class instances (the wording "class
> instances" might be misleading in an OO context... think interface
> implementation instead) to be defined in any module and it hasn't led
> to any serious confusion or other problems that I am aware of. In my
> opinion it is a very important factor in the extensibility of Haskell.
> For instance I'm not sure how the heavily used "Binary" package
> (http://hackage.haskell.org/package/binary) would even be possible
> without this since in relies on being able to declare Binary class
> instances for all the most commonly used types (Int, Char etc). Are
> there different implications for allowing this kind of thing in go (or
> is my comparison off base)?

I think Russ's description below explains the difficulties in Go.

Go does allow interface implementations to be defined in any package.
What it doesn't allow is for one package to define a method for a type
defined in another package.

In Go I think the rough equivalent to Haskell's Binary package would
be Go's encoding/binary package, which simply defines methods which
accept all of the basic types.

That seems like a poor substitute since it disables OOP... Rather than saying that the basic types are serializiable (by implementing the interface for them), you break out the serializer into a separate object which accepts the basic types. But what happens when a third party wants to serialize *his* types? He can't add another method that accepts it to the serializer (because of the restriction you mention), so what does he do? I guess there needs to be *both* an interface for serializable things so that third parties can implement that and become serializiable, *and* a bunch of methods implemented by the author of the serializer library for any types he's aware of that he wants to support. If he forgets one, the user is screwed with no way of remedying it. This seems seems clunky and redundant to me.

This situation is no worse than C++ or Java etc., but you're *so* close to not having any issues at all if you just allowed methods to be defined for any type anywhere. Like someone mentioned, Haskell allows this and this is key to Haskell's extensibility, and very far from being a problem in the slightest. So I think there's plenty of real world experience to alleviate any fears that this would be problematic.


--
Sebastian Sylvan

Quenio

unread,
Nov 14, 2009, 9:08:34 PM11/14/09
to golang-nuts
Hi All,

After my original query, I've started coding with Go a little more,
and I am really finding that, because of the package scope limitation,
the decoupling of types and methods is not useful at all!

I may be missing something here, but doing something like this:

type MyType struct {
myField OtherType;
}

func (self MyType) MyMethod() MyResulType {
return something;
}

Is not different at all from something like:

class MyType {
myField OtherType;
func MyMethod() MyResulType {
return something;
}
}

Especially because both the type and the method have to be in the same
package anyways. In fact, the decoupling only makes you have to type
much more repetitive code when you have several methods:

func (self MyType) MyMethod1() MyResulType {
return something;
}

func (self MyType) MyMethod2() MyResulType {
return something;
}

func (self MyType) MyMethod3() MyResulType {
return something;
}

func (self MyType) MyMethod4() MyResulType {
return something;
}

"(self MyType)" is totally repetitive!

Of course, you can say that you can apply methods to things other then
structs, but this could be valid:

type MyString string {
func MyMethod1() MyResulType {
return something;
}
func MyMethod2() MyResulType {
return something;
}
}

Now, if you were to open the package scope limitation, then I can see
the decoupling making a lot more sense.

Am I missing anything fundamental here? Or, what I just said makes
sense to other folks as well?

Thanks,
Quenio

On Nov 14, 10:54 am, Ian Lance Taylor <i...@google.com> wrote:

Peter Froehlich

unread,
Nov 14, 2009, 9:33:13 PM11/14/09
to Quenio, golang-nuts
I am sure that a large part of this can be explained by the Oberon-2
notation for methods which is exactly the same. As they credit on the
website and as I keep pointing out, Oberon and other Wirth-ian
languages are a big root of Go. It's not currently presented like that
in PR efforts, probably because many C-heads would go "What? Wirth?
Nah, I won't touch that..." :-/
--
Peter H. Froehlich <http://www.cs.jhu.edu/~phf/>
Senior Lecturer | Director, Johns Hopkins Gaming Lab

Ian Lance Taylor

unread,
Nov 15, 2009, 10:45:55 PM11/15/09
to Sebastian Sylvan, Keith Sheppard, r...@golang.org, Quenio, golang-nuts
Sebastian Sylvan <sebastia...@gmail.com> writes:

> This situation is no worse than C++ or Java etc., but you're *so* close to
> not having any issues at all if you just allowed methods to be defined for
> any type anywhere. Like someone mentioned, Haskell allows this and this is
> key to Haskell's extensibility, and very far from being a problem in the
> slightest. So I think there's plenty of real world experience to alleviate
> any fears that this would be problematic.

In Go, whether a conversion from one interface type to another
interface type succeeds at runtime depending on the set of methods
defined for the dynamic type of the interface. If the set of methods
for a type is different in different packages, then whether the
conversion between interface types succeeds depends on exactly where
the value was created. That seems potentially confusing.

So: how does Haskell address this issue? Is it the case in Haskell
that the location where a value is defined affects which interfaces it
satisfies?

When I read about Haskell type classes in more detail, it's not
obvious to me that they provide a feature which Go considers
important: a type may satisfy an interface defined in a different,
independent, package, and that happens automatically with no explicit
declaration, and it is type checked. It may well be that Haskell has
that feature and I don't see it. If Haskell does not have that
feature, then the issue I describe above does not arise in Haskell,
because the program defines whether a particular type satisfies a
particular interface.

Ian

Keith Sheppard

unread,
Nov 15, 2009, 11:42:52 PM11/15/09
to Ian Lance Taylor, Sebastian Sylvan, r...@golang.org, Quenio, golang-nuts
On Sun, Nov 15, 2009 at 10:45 PM, Ian Lance Taylor <ia...@google.com> wrote:

> In Go, whether a conversion from one interface type to another
> interface type succeeds at runtime depending on the set of methods
> defined for the dynamic type of the interface.  If the set of methods
> for a type is different in different packages, then whether the
> conversion between interface types succeeds depends on exactly where
> the value was created.  That seems potentially confusing.

In Haskell this is the case (if I understand you right). So lets say I
have something like:

- in MyModule1 I implement the Binary interface for String
- in MyModule2 I define a different Binary interface for String
- in MyModule3 I don't define any Binary instance for String (and also
do not import a Binary instance from another module)

That means that MyModule1 and MyModule2 can both treat String as a
Binary (with different Binary behaviors in each module), and I cannot
treat string as a Binary in MyModule3... and this is true even when
the same String reference is used across my 3 modules. Looking at this
from the outside it sounds very confusing! It really is not though
when you're working in the code and it's hard for me to describe why.

>
> So: how does Haskell address this issue?  Is it the case in Haskell
> that the location where a value is defined affects which interfaces it
> satisfies?
>
> When I read about Haskell type classes in more detail, it's not
> obvious to me that they provide a feature which Go considers
> important: a type may satisfy an interface defined in a different,
> independent, package, and that happens automatically with no explicit
> declaration, and it is type checked.

In Haskell it is typed checked, but also requires explicitly defining
the interface implementation (in any module). I'm probably missing
something but I don't understand why that makes a difference... I
think you're referring to the strong duck typing (correct me if I'm
abusing the term here) vs the more explicit typing in Haskell. Maybe
it will occur to me when I play around more with go though.

> It may well be that Haskell has
> that feature and I don't see it.  If Haskell does not have that
> feature, then the issue I describe above does not arise in Haskell,
> because the program defines whether a particular type satisfies a
> particular interface.
>
> Ian
>

Best,
Keith

--
keithsheppard.name

konrad

unread,
Nov 15, 2009, 11:54:28 PM11/15/09
to golang-nuts
Being able to add random mathods to well known types is one of the
things that everyone hates about javascript. It sounds like a good
Idea when you do it. But will eventually lead you to curse profusly
when you import some package which addeds a host of bizzar
functionality to every int and string in existence.

If this became a feature of the language import would end up having
unpredictable side effects pretty well anywhere. Worst still you could
end up with all sorts of weird hard to trace depencencies if Package B
rellies on some methods being added to String by package A but does
not actually need to import package A.

- konrad

Sean

unread,
Nov 16, 2009, 12:09:42 AM11/16/09
to golang-nuts
I like the way it is. Adding methods to well known types is just way
too confusing.

Ian Lance Taylor

unread,
Nov 16, 2009, 1:04:06 AM11/16/09
to Keith Sheppard, Sebastian Sylvan, r...@golang.org, Quenio, golang-nuts
Keith Sheppard <keit...@gmail.com> writes:

> On Sun, Nov 15, 2009 at 10:45 PM, Ian Lance Taylor <ia...@google.com> wrote:
>
>> In Go, whether a conversion from one interface type to another
>> interface type succeeds at runtime depending on the set of methods
>> defined for the dynamic type of the interface.  If the set of methods
>> for a type is different in different packages, then whether the
>> conversion between interface types succeeds depends on exactly where
>> the value was created.  That seems potentially confusing.
>
> In Haskell this is the case (if I understand you right). So lets say I
> have something like:
>
> - in MyModule1 I implement the Binary interface for String
> - in MyModule2 I define a different Binary interface for String
> - in MyModule3 I don't define any Binary instance for String (and also
> do not import a Binary instance from another module)
>
> That means that MyModule1 and MyModule2 can both treat String as a
> Binary (with different Binary behaviors in each module), and I cannot
> treat string as a Binary in MyModule3... and this is true even when
> the same String reference is used across my 3 modules. Looking at this
> from the outside it sounds very confusing! It really is not though
> when you're working in the code and it's hard for me to describe why.

In Go, let's say MyModule3 has a function which takes a parameter of
type interface {} (the empty interface). Let's say MyModule3 converts
that value to the Binary interface used by MyModule2. Let's say
MyModule1 passes a String to MyModule3. What should happen when
MyModule3 converts the value to MyModule2's Binary interface?


>> So: how does Haskell address this issue?  Is it the case in Haskell
>> that the location where a value is defined affects which interfaces it
>> satisfies?
>>
>> When I read about Haskell type classes in more detail, it's not
>> obvious to me that they provide a feature which Go considers
>> important: a type may satisfy an interface defined in a different,
>> independent, package, and that happens automatically with no explicit
>> declaration, and it is type checked.
>
> In Haskell it is typed checked, but also requires explicitly defining
> the interface implementation (in any module). I'm probably missing
> something but I don't understand why that makes a difference... I
> think you're referring to the strong duck typing (correct me if I'm
> abusing the term here) vs the more explicit typing in Haskell. Maybe
> it will occur to me when I play around more with go though.

I think it makes a difference because if you explicitly define which
interface you are definining, then there is no ambiguity if some
piece of code tries to convert the value to an interface: it can only
succed if the value was explicitly defined as satisfying that
interface.

In Go, where an interface is satisfied simply because the type
provides a set of methods, it seems ambiguous what should happen if
the same type can have different set of methods in different packages.

Ian

Sebastian Sylvan

unread,
Nov 16, 2009, 6:11:09 AM11/16/09
to Ian Lance Taylor, Keith Sheppard, r...@golang.org, Quenio, golang-nuts
Haskell decouples types from methods like Go, but allows you to "instantiate" any type in any class at any point. The only real difference is that Haskell requires you to be explicit and say "I now want the type X to instantiate class Y", rather than implicitly gathering it from the methods you have defined. It's unclear how often you would unintentionally instantiate a type in an interface by gathering up methods defined in different modules. I think not very often - the vast majority of cases would surely be where someone deliberately implements the methods required to satisfy some interface, surely?

You still end up in the situation where the "interfaces" a type satisfies depends on which modules you have imported, but this isn't really a problem in practice.

--
Sebastian Sylvan

Ian Lance Taylor

unread,
Nov 16, 2009, 2:06:14 PM11/16/09
to Sebastian Sylvan, Keith Sheppard, r...@golang.org, Quenio, golang-nuts
Yes, definitely, but that isn't the issue. The issue is the
ambiguity.

For example, let's say that package A defines some methods on the type
string, and that that lets the type string satisfy some interface I
that A cares about. Let's say that A hands out string values, and
expects to get them back from other packages, and expects the values
that it gets back to satisfy the interface I. Now let's say that
package B decides to create its own string value and hand it to
package A. Does that value have the methods defined in package A or
not? Now support that package B gets the value to package A by going
through package C which deals with values of the empty interface type
(e.g., container/vector). Now does the value have the methods in
package A? If it does, how does that get implemented? If it doesn't,
isn't that rather confusing for the programmer?

We avoid these issues of ambiguity by prohibiting the case entirely.
The only reasonable way to lift that prohibition is to develop some
approach which resolves the ambiguity cleanly and comprehensibly.

Ian

Sebastian Sylvan

unread,
Nov 16, 2009, 2:16:20 PM11/16/09
to Ian Lance Taylor, Keith Sheppard, r...@golang.org, Quenio, golang-nuts
In Haskell methods aren't a property of the value, they're a property of the type. Any value of that type have all the methods for that property that are in scope. Basically if you import stuff that gives a type interfaces X, Y and Z, then you can use those three interfaces. The values don't change based on what interfaces they don't support. If you want to use the interface X for a given type, then you have to make sure you have those methods available to you by importing it. There's no assumptions that they "come with the value".



--
Sebastian Sylvan

Marcin 'Qrczak' Kowalczyk

unread,
Nov 16, 2009, 2:18:44 PM11/16/09
to Sebastian Sylvan, Ian Lance Taylor, Keith Sheppard, r...@golang.org, Quenio, golang-nuts
2009/11/16 Sebastian Sylvan <sebastia...@gmail.com>:

> In Haskell methods aren't a property of the value, they're a property of the
> type. Any value of that type have all the methods for that property that are
> in scope.

Indeed. You don't even need a value of that type to dispatch on. You
can dispatch on the result type of the function you are writing, or on
the type of elements of an empty list...

--
Marcin Kowalczyk

Ian Lance Taylor

unread,
Nov 16, 2009, 4:23:49 PM11/16/09
to Sebastian Sylvan, Keith Sheppard, r...@golang.org, Quenio, golang-nuts
Sebastian Sylvan <sebastia...@gmail.com> writes:

>> For example, let's say that package A defines some methods on the type
>> string, and that that lets the type string satisfy some interface I
>> that A cares about. Let's say that A hands out string values, and
>> expects to get them back from other packages, and expects the values
>> that it gets back to satisfy the interface I. Now let's say that
>> package B decides to create its own string value and hand it to
>> package A. Does that value have the methods defined in package A or
>> not?
>
>
> In Haskell methods aren't a property of the value, they're a property of the
> type.

The same statement is true of Go.

> Any value of that type have all the methods for that property that are
> in scope. Basically if you import stuff that gives a type interfaces X, Y
> and Z, then you can use those three interfaces. The values don't change
> based on what interfaces they don't support. If you want to use the
> interface X for a given type, then you have to make sure you have those
> methods available to you by importing it. There's no assumptions that they
> "come with the value".

That is fine, but what we are talking about here is adding methods to
a builtin type in one package but not in another package. In effect,
this creates a new type which happens to have the same name as, and be
completely compatible with, an existing type. That's why I started
talking about methods being attached to values--because it's hard to
know how else to talk about them in this situation. In conjunction
with Go's feature of having types match interfaces automatically with
no explicit declaration, and having this be done dynamically at
runtime, we get ambiguities.

I'm trying to explain why, as far as I can tell, the fact that
defining methods on builtin types works out OK in Haskell does not
remove the ambiguities that it would have in Go.

Ian

Sebastian Sylvan

unread,
Nov 16, 2009, 4:31:01 PM11/16/09
to Ian Lance Taylor, Keith Sheppard, r...@golang.org, Quenio, golang-nuts
On Mon, Nov 16, 2009 at 9:23 PM, Ian Lance Taylor <ia...@google.com> wrote:
Sebastian Sylvan <sebastia...@gmail.com> writes:

>> For example, let's say that package A defines some methods on the type
>> string, and that that lets the type string satisfy some interface I
>> that A cares about.  Let's say that A hands out string values, and
>> expects to get them back from other packages, and expects the values
>> that it gets back to satisfy the interface I.  Now let's say that
>> package B decides to create its own string value and hand it to
>> package A.  Does that value have the methods defined in package A or
>> not?
>
>
> In Haskell methods aren't a property of the value, they're a property of the
> type.

The same statement is true of Go.

> Any value of that type have all the methods for that property that are
> in scope. Basically if you import stuff that gives a type interfaces X, Y
> and Z, then you can use those three interfaces. The values don't change
> based on what interfaces they don't support. If you want to use the
> interface X for a given type, then you have to make sure you have those
> methods available to you by importing it. There's no assumptions that they
> "come with the value".

That is fine, but what we are talking about here is adding methods to
a builtin type in one package but not in another package.  In effect,
this creates a new type which happens to have the same name as, and be
completely compatible with, an existing type.

In what sense does it create a new type? The type is the same, adding/removing functions acting on the type to/from the scope doesn't change the type.

 
I'm trying to explain why, as far as I can tell, the fact that
defining methods on builtin types works out OK in Haskell does not
remove the ambiguities that it would have in Go.

I'm still not sure how it's different. I can instantiate e.g. String into my Foo class in one module and into my Bar class in another, and it all works fine. If a module needs both those classes it will need to import both the modules, if it only needs one or the other it can import one or the other.
 
 
--
Sebastian Sylvan

Ian Lance Taylor

unread,
Nov 16, 2009, 5:31:32 PM11/16/09
to Sebastian Sylvan, Keith Sheppard, r...@golang.org, Quenio, golang-nuts
Sebastian Sylvan <sebastia...@gmail.com> writes:

>> > In Haskell methods aren't a property of the value, they're a property of
>> the
>> > type.
>>
>> The same statement is true of Go.
>>
>> > Any value of that type have all the methods for that property that are
>> > in scope. Basically if you import stuff that gives a type interfaces X, Y
>> > and Z, then you can use those three interfaces. The values don't change
>> > based on what interfaces they don't support. If you want to use the
>> > interface X for a given type, then you have to make sure you have those
>> > methods available to you by importing it. There's no assumptions that
>> they
>> > "come with the value".
>>
>> That is fine, but what we are talking about here is adding methods to
>> a builtin type in one package but not in another package. In effect,
>> this creates a new type which happens to have the same name as, and be
>> completely compatible with, an existing type.
>
>
> In what sense does it create a new type? The type is the same,
> adding/removing functions acting on the type to/from the scope doesn't
> change the type.

As you said above: the set of methods is a property of the type. If
you change the set of methods, you have a new type.


>> I'm trying to explain why, as far as I can tell, the fact that
>> defining methods on builtin types works out OK in Haskell does not
>> remove the ambiguities that it would have in Go.
>
>
> I'm still not sure how it's different. I can instantiate e.g. String into my
> Foo class in one module and into my Bar class in another, and it all works
> fine. If a module needs both those classes it will need to import both the
> modules, if it only needs one or the other it can import one or the other.

The issue comes when you move values between the packages via the
empty interface. Let me give a concrete example:

==================================================

package P1
import "fmt"
func (s string) First() int { return s[0] }
func Print(v interface {}) { fmt.Println(v.(string).First()) }

==================================================

package P2
func (s string) First() int { return s[1] }
func Get() string { return "123" }

==================================================

package P3
func Convert(s string) interface{} { return (interface{})(s) }

==================================================

package main
import ( "P1"; "P2" )
func main() {
P1.Print("abc");
P1.Print(P2.Get());
P1.Print(P3.Convert("abc"));
P1.Print(P3.Convert(P2.Get()));
}

==================================================

What does that program print, and why is that a sensible choice?

Ian

Keith Sheppard

unread,
Nov 16, 2009, 10:32:52 PM11/16/09
to Ian Lance Taylor, Sebastian Sylvan, r...@golang.org, Quenio, golang-nuts
Here is what makes most sense for me: For deciding which interface
implementation to use for an object choose the implementation that is
in scope (in the namespace) at the point in the code where that object
is required to take on that interface type (hopefully that wording
makes sense). If there is more than one implementation in scope and
the code tries to use the object as a type of the duplicated interface
then I think that should be a compile-time error.

So with this in mind, it doesn't matter where the string is created
only where and how it is used. Since First() is only called in P1 the
P1 implementation should always be used for all 4 cases.

I think that reflection might kill this though (maybe this is a part
of what you were saying). Haskell doesn't have reflection, and I
assume that with go's reflection objects must be able to describe
themselves even when the types are not in scope. If this is the case
then I think the type info must belong to the object instance and I
can't see how it's possible to allow for multiple interface impls per
object in a reasonable way...

Best
Keith
--
keithsheppard.name

Ben Polidore

unread,
Nov 17, 2009, 12:16:21 AM11/17/09
to golang-nuts
I agree. I think Microsoft's implementation of extension methods
works well in C#:

http://msdn.microsoft.com/en-us/library/bb383977.aspx

And I think it would be consistent with Go's design paradigm to add
this feature. Without it, you are basically just subclassing when you
do something like this:

type MyString string;

func (s MyString)Print() {}

Not much different than:

class MyString : String
{
void Print() {}
}

On Nov 14, 9:08 pm, Quenio <queniodossan...@gmail.com> wrote:
> Hi All,
>
> After my original query, I've started coding with Go a little more,
> and I am really finding that, because of the package scope limitation,
> the decoupling of types andmethodsis not useful at all!
>
> I may be missing something here, but doing something like this:
>
> typeMyType struct {
>    myField OtherType;
>
> }
>
> func (self MyType) MyMethod() MyResulType {
>    return something;
>
> }
>
> Is not different at all from something like:
>
> class MyType {
>    myField OtherType;
>    func MyMethod() MyResulType {
>       return something;
>    }
>
> }
>
> Especially because both thetypeand the method have to be in the same
> package anyways. In fact, the decoupling only makes you have totype
> much more repetitive code when you have severalmethods:
>
> func (self MyType) MyMethod1() MyResulType {
>    return something;
>
> }
>
> func (self MyType) MyMethod2() MyResulType {
>    return something;
>
> }
>
> func (self MyType) MyMethod3() MyResulType {
>    return something;
>
> }
>
> func (self MyType) MyMethod4() MyResulType {
>    return something;
>
> }
>
> "(self MyType)" is totally repetitive!
>
> Of course, you can say that you can applymethodsto things other then
> structs, but this could be valid:
>
> typeMyString string {
> > >>> Should I use my own stringtypein all my API's?
>
> > >> No.
>
> > >>> It would be unfamiliar tonewAPI users to see atypethat just so
> > >>> happens to be a string.
>
> > >>> I shouldn't have to use a customtypein my API just so that I have
> > >>> some convenience in my API's implementation.
>
> > >>> A customtypeshould only be used if it will make the API more useful,
> > >>> clear to its users.
>
> > >>> Also, if every API provider decides to have its own custom version of
> > >>> string, how much more confusing will it be for users of those APIs.
>
> > >> These are all good reasons for the answer to be no.
>
> > >> Getting back to your original question, about being able todefine
> > >>methodsfor other package's types, the fundamental problem is that
> > >> this leads to confusion during dynamic interface checks.
> > >> For example, suppose you like seeing ints print in hexadecimal,
> > >> so youdefinea method func (i int) String() string that returns
> > >> a hexadecimal string.  I prefer octal, so Idefinemy own

Ian Lance Taylor

unread,
Nov 17, 2009, 12:20:40 AM11/17/09
to Keith Sheppard, Sebastian Sylvan, r...@golang.org, Quenio, golang-nuts
Keith Sheppard <keit...@gmail.com> writes:

> Here is what makes most sense for me: For deciding which interface
> implementation to use for an object choose the implementation that is
> in scope (in the namespace) at the point in the code where that object
> is required to take on that interface type (hopefully that wording
> makes sense). If there is more than one implementation in scope and
> the code tries to use the object as a type of the duplicated interface
> then I think that should be a compile-time error.
>
> So with this in mind, it doesn't matter where the string is created
> only where and how it is used. Since First() is only called in P1 the
> P1 implementation should always be used for all 4 cases.
>
> I think that reflection might kill this though (maybe this is a part
> of what you were saying). Haskell doesn't have reflection, and I
> assume that with go's reflection objects must be able to describe
> themselves even when the types are not in scope. If this is the case
> then I think the type info must belong to the object instance and I
> can't see how it's possible to allow for multiple interface impls per
> object in a reasonable way...

That is the basic problem: the implementation of interfaces is that
values carry their types with them, even when those types are not in
scope.

Ian

Sebastian Sylvan

unread,
Nov 17, 2009, 3:30:13 AM11/17/09
to Ian Lance Taylor, Keith Sheppard, r...@golang.org, Quenio, golang-nuts
Right. So that's what I was getting at. In Haskell the methods are a property of the *type*, not the value. The method dictionary is passed separately from the value (and depend on what instances are in scope at that point). This is type based dispatch versus value based dispatch (like someone mentioned, in Haskell you can even dispatch on the type of the *return value* - so you don't even have a value to lookup the vtable from!).

So I guess it would be harder to sort this out in Go. Someone else mentioned C#'s extension methods which is probably a decent model to look at. Being able to extend existing types is a pretty fantastic feature in practice.


--
Sebastian Sylvan

Ian Lance Taylor

unread,
Nov 17, 2009, 10:55:02 AM11/17/09
to Sebastian Sylvan, Keith Sheppard, r...@golang.org, Quenio, golang-nuts
Sebastian Sylvan <sebastia...@gmail.com> writes:

>> That is the basic problem: the implementation of interfaces is that
>> values carry their types with them, even when those types are not in
>> scope.
>
>
> Right. So that's what I was getting at. In Haskell the methods are a
> property of the *type*, not the value. The method dictionary is passed
> separately from the value (and depend on what instances are in scope at that
> point). This is type based dispatch versus value based dispatch (like
> someone mentioned, in Haskell you can even dispatch on the type of the
> *return value* - so you don't even have a value to lookup the vtable from!).

I'm going to say it one more time and then quit: it is also true in Go
that methods are a property of the type. Go uses type-based dispatch.
It does not use value-based dispatch.

The reason we are getting mixed up is that in Go when you add methods
to a type you get a new type. When you want to dispatch on a value
extracted from interface{}, you get the type of that value. What
other type could you possibly use in that scenario?


> So I guess it would be harder to sort this out in Go. Someone else mentioned
> C#'s extension methods which is probably a decent model to look at. Being
> able to extend existing types is a pretty fantastic feature in practice.

Go lets you extend existing types, it's just that you get a new type
when you do it. If somebody can figure out a good way to do this
without getting a new type, and which avoids the ambiguities due to
Go's use of automatically matching types and interfaces, then we are
certainly interested.

Ian

roger peppe

unread,
Nov 17, 2009, 12:25:21 PM11/17/09
to golang-nuts
2009/11/17 Sebastian Sylvan <sebastia...@gmail.com>:
> Right. So that's what I was getting at. In Haskell the methods are a
> property of the *type*, not the value.

go's interfaces are more akin to haskell's Data.Dynamic
rather than its type classes.

that's why you can't cast from chan X to chan Y even when
type X Y;
because each element coming out of the channel has its own
method dictionary.

haskell type classes are closely allied to generics,
which as we all know, go doesn't have yet.

adam_smith

unread,
Nov 22, 2009, 5:11:14 PM11/22/09
to golang-nuts
I don't see a problem with Go's current approach. What is wrong with
adding free functions to add functionality to a type?

I know I used to be against that before. But the main reason was just
that
I thought it looked nicer with methods. But Myers Effective C++ Item
23 made me change my view. By having the core functionality of a class
(in Go's case a Type / struct) defined as methods
and the rest defined as free functions, it becomes easier to
understand that type. When reading code you know that a free function
can only perform its jobs by calling other free functions or methods
of the type. It can't have access to the type's private member data.
Free functions also makes it easier to share functionality between two
types, since the function could work on a interface the two types
share rather than a concrete type.

On Nov 17, 4:55 pm, Ian Lance Taylor <i...@google.com> wrote:
> Sebastian Sylvan <sebastian.syl...@gmail.com> writes:
> >> That is the basic problem: the implementation of interfaces is that
> >> values carry their types with them, even when those types are not in
> >> scope.
>
> > Right. So that's what I was getting at. InHaskellthe methods are a
> > property of the *type*, not the value. The method dictionary is passed
> > separately from the value (and depend on what instances are in scope at that
> > point). This is type based dispatch versus value based dispatch (like
> > someone mentioned, inHaskellyou can even dispatch on the type of the

ziyu_huang

unread,
Nov 22, 2009, 9:01:28 PM11/22/09
to golang-nuts
Since there is no macro support in Go. It's better to provide some way
to ease of programming, such as extend a type *locally*.
The dependency is hard to support if you "extend a type in other
package" in public package system, but if it is just *local", I think
it is possible to do it.
I mean if I can have a way simplify my code(just like macro) that
would be useful, even the extension of a type can be used only in my
local package.

On 11月17日, 上午3時06分, Ian Lance Taylor <i...@google.com> wrote:
> Sebastian Sylvan <sebastian.syl...@gmail.com> writes:
Reply all
Reply to author
Forward
0 new messages