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:
On Thu, Nov 12, 2009 at 7:18 PM, Quenio <queniodossan...@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.
> On Thu, Nov 12, 2009 at 7:18 PM, Quenio <queniodossan...@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.
Quenio <queniodossan...@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.
> 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.
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.
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.
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,
> and use string in the public functions.
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,
> > 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.
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)?
On Fri, Nov 13, 2009 at 12:18 AM, Russ Cox <r...@golang.org> wrote:
>> 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.
Keith Sheppard <keiths...@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.
> On Fri, Nov 13, 2009 at 12:18 AM, Russ Cox <r...@golang.org> wrote:
>>> 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.
> 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.
> > 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.
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:
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:
> Keith Sheppard <keiths...@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
> > On Fri, Nov 13, 2009 at 12:18 AM, Russ Cox <r...@golang.org> wrote:
> >>> 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.
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..." :-/
On Sat, Nov 14, 2009 at 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 and methods is not useful at all!
> I may be missing something here, but doing something like this:
> 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:
> 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:
>> Keith Sheppard <keiths...@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
>> > On Fri, Nov 13, 2009 at 12:18 AM, Russ Cox <r...@golang.org> wrote:
>> >>> 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.
Sebastian Sylvan <sebastian.syl...@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.
On Sun, Nov 15, 2009 at 10:45 PM, Ian Lance Taylor <i...@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.
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
On Nov 12, 2:18 pm, Quenio <queniodossan...@gmail.com> wrote:
Keith Sheppard <keiths...@gmail.com> writes:
> On Sun, Nov 15, 2009 at 10:45 PM, Ian Lance Taylor <i...@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.
> Sebastian Sylvan <sebastian.syl...@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.
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 <sebastian.syl...@gmail.com> writes:
> On Mon, Nov 16, 2009 at 3:45 AM, Ian Lance Taylor <i...@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.
>> 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.
> 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?
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.