FAQ
When evaluating whether a type satisfies an interface, the Go compiler checks whether the function definitions match *exactly*. If an interface X specifies a function F that returns an interface Y, you can't satisfy the interface X with a type that has a function F that returns a value that satisfies interface Y but is not explicitly interface Y.
Not having the compiler understand this is making my APIs more complicated and adding unnecessary adapter objects and/or typechecks to my code.
Since that might be a little abstract, here's a succint example of what I'm suggesting should work because LibErr implements the error interface.
type LibErr struct {}func (e *LibErr) Error() string { return "" }
type LibReader struct {}func (r *LibReader) Read(p []byte) (int, *LibErr) { return 0, nil }
ioutil.ReadAll(new(LibReader))

Is there a compelling reason I'm overlooking for why interface satisfaction rules don't permit this?

--
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+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Search Discussions

  • Tamás Gulácsi at Nov 26, 2013 at 7:10 pm
    Because your Read function is not what is defined in the Reader interface.
    If you define it to return int64,error then itwill work, even when you return *LibErr.

    An interface defines what has to be implemented, nothing else. Thus an interface cannot implement another interfac.

    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/groups/opt_out.
  • Alan Shreve at Nov 26, 2013 at 7:17 pm
    The Reader interface defines a Read function which returns int, not int64: http://golang.org/pkg/io/#ReaderThe compiler error proves this as well:
    ./t.go:11: cannot use new(LibReader) (type *LibReader) as type io.Reader in function argument: *LibReader does not implement io.Reader (wrong type for Read method) have Read([]byte) (int, *LibErr) want Read([]byte) (int, error)
    Date: Tue, 26 Nov 2013 11:10:45 -0800
    From: tgulacsi78@gmail.com
    To: golang-nuts@googlegroups.com
    Subject: [go-nuts] Implicit interface satisfaction of parameters and return values

    Because your Read function is not what is defined in the Reader interface.
    If you define it to return int64,error then itwill work, even when you return *LibErr.

    An interface defines what has to be implemented, nothing else. Thus an interface cannot implement another interfac.

    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/groups/opt_out.

    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/groups/opt_out.
  • Ian Lance Taylor at Nov 26, 2013 at 7:23 pm

    On Tue, Nov 26, 2013 at 10:54 AM, Alan Shreve wrote:
    When evaluating whether a type satisfies an interface, the Go compiler
    checks whether the function definitions match *exactly*. If an interface X
    specifies a function F that returns an interface Y, you can't satisfy the
    interface X with a type that has a function F that returns a value that
    satisfies interface Y but is not explicitly interface Y.

    Not having the compiler understand this is making my APIs more complicated
    and adding unnecessary adapter objects and/or typechecks to my code.

    Since that might be a little abstract, here's a succint example of what I'm
    suggesting should work because LibErr implements the error interface.

    type LibErr struct {}
    func (e *LibErr) Error() string { return "" }

    type LibReader struct {}
    func (r *LibReader) Read(p []byte) (int, *LibErr) { return 0, nil }

    ioutil.ReadAll(new(LibReader))


    Is there a compelling reason I'm overlooking for why interface satisfaction
    rules don't permit this?
    The representation of *LibErr in memory is not the same as the
    representation of error. You need to have some code to explicitly
    convert *LibErr to error. Calling a method on an interface simply
    calls the method of the interface's dynamic type. So what you are
    suggesting would require some code to convert *LibErr to error, but
    there is no place for that code to live.

    This is perhaps more obvious when you consider converting *LibErr to
    interface{} and then converting it to io.Reader.

    Ian

    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/groups/opt_out.
  • Alan Shreve at Nov 26, 2013 at 8:33 pm
    Thanks for the explanation. After I remembered the more formal term describing this "covariant return type" I was able to find and older thread and rsc's description of Go's interfaces implementation which helped me visualize what you meant about the memory representations being different and why this isn't trivial.
    Links for anyone else interested:https://groups.google.com/forum/#!topic/golang-nuts/fXwE3dvFC2chttp://research.swtch.com/interfaces
    Date: Tue, 26 Nov 2013 11:22:56 -0800
    Subject: Re: [go-nuts] Implicit interface satisfaction of parameters and return values
    From: iant@golang.org
    To: alan@inconshreveable.com
    CC: golang-nuts@googlegroups.com
    On Tue, Nov 26, 2013 at 10:54 AM, Alan Shreve wrote:
    When evaluating whether a type satisfies an interface, the Go compiler
    checks whether the function definitions match *exactly*. If an interface X
    specifies a function F that returns an interface Y, you can't satisfy the
    interface X with a type that has a function F that returns a value that
    satisfies interface Y but is not explicitly interface Y.

    Not having the compiler understand this is making my APIs more complicated
    and adding unnecessary adapter objects and/or typechecks to my code.

    Since that might be a little abstract, here's a succint example of what I'm
    suggesting should work because LibErr implements the error interface.

    type LibErr struct {}
    func (e *LibErr) Error() string { return "" }

    type LibReader struct {}
    func (r *LibReader) Read(p []byte) (int, *LibErr) { return 0, nil }

    ioutil.ReadAll(new(LibReader))


    Is there a compelling reason I'm overlooking for why interface satisfaction
    rules don't permit this?
    The representation of *LibErr in memory is not the same as the
    representation of error. You need to have some code to explicitly
    convert *LibErr to error. Calling a method on an interface simply
    calls the method of the interface's dynamic type. So what you are
    suggesting would require some code to convert *LibErr to error, but
    there is no place for that code to live.

    This is perhaps more obvious when you consider converting *LibErr to
    interface{} and then converting it to io.Reader.

    Ian

    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/groups/opt_out.

    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/groups/opt_out.
  • Kevin Gillette at Nov 27, 2013 at 1:31 am
    Some cases could theoretically be detected and statically wrapped by the
    compiler such that existing calling conventions are maintained, e.g.

    var r io.Reader = new(LibErr)

    However, there are other situations...

    func Dynamic(v interface{}, f func(interface{}) string) { fmt.Println(f(v))
    }

    func F(v interface{}) string { return v.(error).Error() }

    Dynamic(new(LibErr), F)

    In the above case, there's no trivial way to determine at compile time that
    LibErr is going to be type-asserted as an error, so there'd be no reason
    for the compiler to generate an error wrapper for it, thus the type
    assertion would fail, and there'd be a clear disparity between behaviors
    based on whether a type *obviously* conforms to an interface loosely in
    some context or not; thus far, the Go team has chosen not to implement
    features with major 'gotcha' mechanics, and this would certainly be one of
    them.

    The only other clear way to deal with this, as with some approaches to
    generics, is by bloating the binary (and reducing compile and runtimes
    significantly) by generating wrappers for every conceivable interface a
    type *could* implement using the proposed relaxed rules, while forcing all
    interface assignments to go through a dynamic wrapper resolution phase.
    With an initial implementation of the kitchen-sink approach, you could
    expect current 5mb Go binaries to explode to be dozens of mb in size just
    to accommodate the feature thoroughly.
    On Tuesday, November 26, 2013 1:33:11 PM UTC-7, Alan Shreve wrote:

    Thanks for the explanation. After I remembered the more formal term
    describing this "covariant return type" I was able to find and older thread
    and rsc's description of Go's interfaces implementation which helped me
    visualize what you meant about the memory representations being different
    and why this isn't trivial.

    Links for anyone else interested:
    https://groups.google.com/forum/#!topic/golang-nuts/fXwE3dvFC2c
    http://research.swtch.com/interfaces
    Date: Tue, 26 Nov 2013 11:22:56 -0800
    Subject: Re: [go-nuts] Implicit interface satisfaction of parameters and
    return values
    From: ia...@golang.org <javascript:>
    To: al...@inconshreveable.com <javascript:>
    CC: golan...@googlegroups.com <javascript:>
    On Tue, Nov 26, 2013 at 10:54 AM, Alan Shreve wrote:
    When evaluating whether a type satisfies an interface, the Go compiler
    checks whether the function definitions match *exactly*. If an
    interface X
    specifies a function F that returns an interface Y, you can't satisfy
    the
    interface X with a type that has a function F that returns a value that
    satisfies interface Y but is not explicitly interface Y.

    Not having the compiler understand this is making my APIs more
    complicated
    and adding unnecessary adapter objects and/or typechecks to my code.

    Since that might be a little abstract, here's a succint example of
    what I'm
    suggesting should work because LibErr implements the error interface.

    type LibErr struct {}
    func (e *LibErr) Error() string { return "" }

    type LibReader struct {}
    func (r *LibReader) Read(p []byte) (int, *LibErr) { return 0, nil }

    ioutil.ReadAll(new(LibReader))


    Is there a compelling reason I'm overlooking for why interface
    satisfaction
    rules don't permit this?
    The representation of *LibErr in memory is not the same as the
    representation of error. You need to have some code to explicitly
    convert *LibErr to error. Calling a method on an interface simply
    calls the method of the interface's dynamic type. So what you are
    suggesting would require some code to convert *LibErr to error, but
    there is no place for that code to live.

    This is perhaps more obvious when you consider converting *LibErr to
    interface{} and then converting it to io.Reader.

    Ian

    --
    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 <javascript:>.
    For more options, visit https://groups.google.com/groups/opt_out.
    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/groups/opt_out.
  • Kevin Gillette at Nov 27, 2013 at 1:36 am
    Ooops. With the second example, I meant to continue the Reader analogy,
    since your *LibErr is without doubt already a valid error implementation.
    Main lesson is that any situation involving first-class functions is going
    to be the current boundary beyond which the compiler can't or won't try to
    do analysis at depth (and even if it could or did, there is generally a
    combinatorial effect that results from that kind of analysis).
    On Tuesday, November 26, 2013 6:31:48 PM UTC-7, Kevin Gillette wrote:

    Some cases could theoretically be detected and statically wrapped by the
    compiler such that existing calling conventions are maintained, e.g.

    var r io.Reader = new(LibErr)

    However, there are other situations...

    func Dynamic(v interface{}, f func(interface{}) string) {
    fmt.Println(f(v)) }

    func F(v interface{}) string { return v.(error).Error() }

    Dynamic(new(LibErr), F)

    In the above case, there's no trivial way to determine at compile time
    that LibErr is going to be type-asserted as an error, so there'd be no
    reason for the compiler to generate an error wrapper for it, thus the type
    assertion would fail, and there'd be a clear disparity between behaviors
    based on whether a type *obviously* conforms to an interface loosely in
    some context or not; thus far, the Go team has chosen not to implement
    features with major 'gotcha' mechanics, and this would certainly be one of
    them.

    The only other clear way to deal with this, as with some approaches to
    generics, is by bloating the binary (and reducing compile and runtimes
    significantly) by generating wrappers for every conceivable interface a
    type *could* implement using the proposed relaxed rules, while forcing
    all interface assignments to go through a dynamic wrapper resolution phase.
    With an initial implementation of the kitchen-sink approach, you could
    expect current 5mb Go binaries to explode to be dozens of mb in size just
    to accommodate the feature thoroughly.
    On Tuesday, November 26, 2013 1:33:11 PM UTC-7, Alan Shreve wrote:

    Thanks for the explanation. After I remembered the more formal term
    describing this "covariant return type" I was able to find and older thread
    and rsc's description of Go's interfaces implementation which helped me
    visualize what you meant about the memory representations being different
    and why this isn't trivial.

    Links for anyone else interested:
    https://groups.google.com/forum/#!topic/golang-nuts/fXwE3dvFC2c
    http://research.swtch.com/interfaces
    Date: Tue, 26 Nov 2013 11:22:56 -0800
    Subject: Re: [go-nuts] Implicit interface satisfaction of parameters
    and return values
    From: ia...@golang.org
    To: al...@inconshreveable.com
    CC: golan...@googlegroups.com

    On Tue, Nov 26, 2013 at 10:54 AM, Alan Shreve <
    al...@inconshreveable.com> wrote:
    When evaluating whether a type satisfies an interface, the Go compiler
    checks whether the function definitions match *exactly*. If an
    interface X
    specifies a function F that returns an interface Y, you can't satisfy
    the
    interface X with a type that has a function F that returns a value
    that
    satisfies interface Y but is not explicitly interface Y.

    Not having the compiler understand this is making my APIs more
    complicated
    and adding unnecessary adapter objects and/or typechecks to my code.

    Since that might be a little abstract, here's a succint example of
    what I'm
    suggesting should work because LibErr implements the error interface.

    type LibErr struct {}
    func (e *LibErr) Error() string { return "" }

    type LibReader struct {}
    func (r *LibReader) Read(p []byte) (int, *LibErr) { return 0, nil }

    ioutil.ReadAll(new(LibReader))


    Is there a compelling reason I'm overlooking for why interface
    satisfaction
    rules don't permit this?
    The representation of *LibErr in memory is not the same as the
    representation of error. You need to have some code to explicitly
    convert *LibErr to error. Calling a method on an interface simply
    calls the method of the interface's dynamic type. So what you are
    suggesting would require some code to convert *LibErr to error, but
    there is no place for that code to live.

    This is perhaps more obvious when you consider converting *LibErr to
    interface{} and then converting it to io.Reader.

    Ian

    --
    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.
    --
    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+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/groups/opt_out.

Related Discussions

Discussion Navigation
viewthread | post
Discussion Overview
groupgolang-nuts @
categoriesgo
postedNov 26, '13 at 6:54p
activeNov 27, '13 at 1:36a
posts7
users4
websitegolang.org

People

Translate

site design / logo © 2022 Grokbase