New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proposal: Default implementation for interface #16254

Closed
mkideal opened this Issue Jul 3, 2016 · 26 comments

Comments

Projects
None yet
10 participants
@mkideal

mkideal commented Jul 3, 2016

Can we support default implementation for interface?

Here is an example:

type Transaction interface {
    Begin()
    Exec() error
    End()
}

func (tr Transaction) Process() error {
    tr.Begin()
    if err := tr.Exec(); err != nil {
        return err
    }
    tr.End()
    return nil
}

Now, if I have a struct DBTransaction which implements Begin(),Exec() error and End() methods(i.e. implements interface Transaction), then DBTransaction automatically owns Process() error method while it's used as Transaction.

@mkideal mkideal changed the title from Default implementation for interface to proposal: Default implementation for interface Jul 3, 2016

@mezoni

This comment has been minimized.

mezoni commented Jul 3, 2016

then DBTransaction automatically owns Process() error method while it's used as Transaction

What means automatically owns if this method does not specified in the type declaration (interface)?

type Transaction interface {
    Begin()
    Exec() error
    End()
    // Process() error 
}

Sorry, but I cannot understand how an interface can have implementation?

@mkideal

This comment has been minimized.

mkideal commented Jul 3, 2016

Did you know trait in rust language(@mezoni)? It's very similar to interface in golang, but support default implementation.

trait Transaction {
    fn begin(&self);
    fn exec(&self) -> Result;
    fn end(&self);
    fn process(&self) -> Result {
        ...
    }
}

And pure virtual class in c++

class Transaction {
    public:
        virtual void begin() = 0;
        virtual int exec() = 0;
        virtual void end() = 0;
        int process() {
            ...
        }
};
@mezoni

This comment has been minimized.

mezoni commented Jul 3, 2016

Did you know trait in rust language(@mezoni)? It's very similar to interface in golang, but support default implementation.

Mixins, traits used in OOP.
Also I know abstract classes.
Also I know that in Go language there are no such thing as OOP and inheritance.
What benefits from the mixins and traits in the Go language if the Go language does not supports inheritance?
Also I know that the Go language does not supports abstract and virtual methods only because they are useless in the Go language (without inheritance).

P.S.
In computer programming, a trait is a concept used in object-oriented programming, which represents a set of methods that can be used to extend the functionality of a class.

I think that these terms are not an applicable to the Go language.

  • Object-oriented programming
  • Class

The Go language supports only a value based types.
Most of them are bits based structs.
Even an int16 type is only a struct with a length of the 16 bits.
This is the main advantage: a value based types with support of references (pointers).

@creker

This comment has been minimized.

creker commented Jul 3, 2016

You can achieve kind of the same thing with embedding. Here's an example

type II interface {
    foo() int
    bar() int
}

//empty struct with shared foo() implementation
type S1 struct {
}
func (s S1) foo() int {
    return 123
}

type S2 struct {
    S1
}

func (s S2) bar() int {
    return 456
}

func main() {
    var v II
    v = S2{}
    fmt.Println(v.foo(), v.bar())
}

https://play.golang.org/p/MHo14zUpq1

Given that interfaces are implemented implicitly, it would be difficult to track down where exactly all the methods are implemented with this proposal.

@mkideal

This comment has been minimized.

mkideal commented Jul 3, 2016

In essence, func (tr Transaction) Process() error just a syntactic sugar of func Process(tr Transaction) error

For example, Reader interface of package io:

type Reader interface {
    Read(b []byte) (n int, err error)
}

func ReadFull(r Reader, buf []byte) (n int, err error) { ... }

Can we use ReadFull function as a method of Reader:

func (r Reader) ReadFull(buf []byte) (n int, err error) { ... }

It's just a syntactic sugar

Certainly, Any structure which implements Reader can overide ReadFull method.

@starius

This comment has been minimized.

starius commented Jul 3, 2016

If your proposal is implemented, one will be able to add any methods to any types by creating an empty interface with desired methods with default implementation, because any type satisfies the empty interface.

@mkideal

This comment has been minimized.

mkideal commented Jul 3, 2016

one will be able to add any methods to any types by creating an empty interface with desired methods

Can you tell how to to this?
Can you write a example?

If you write:

type Any interface {}

func (any Any) MyMethod() { ... }

In this case, MyMethod is one method of Any only.

@adg

This comment has been minimized.

Contributor

adg commented Jul 4, 2016

What problem does this proposal solve?

@mezoni

This comment has been minimized.

mezoni commented Jul 4, 2016

What problem does this proposal solve?

Possible the idea of this proposal (default implementation for interface) is useful but this is impossible in the Go language, with it it structural type system.
Initially, I also cannot undestood why Go language does not allowed some concepts (based on the inheritance) wihich are works very well in the nominative type systems.

This is because nominal subtyping means that one type is a subtype of another if and only if it is explicitly declared to be so in its definition.

In structural typing, an element is considered to be compatible with another if, for each feature within the second element's type, a corresponding and identical feature exists in the first element's type.

That is, this is impossible to have a default implementation only because it (implementation of the interface, as the first element) should be defined explictly in the second element (interface implementer) to be compatible with the interface, as the first element.

@starius

This comment has been minimized.

starius commented Jul 4, 2016

one will be able to add any methods to any types by creating an empty interface with desired methods

Can you tell how to to this?
Can you write a example?

I'll continue your example:

type Any interface {}

func (any Any) MyMethod() { ... }

type Foo struct {
    // some members
}

func main() {
    var foo Foo
    foo.MyMethod()
}

In this example, Foo satisfies Any interface and (according to your proposal) gets default method MyMethod from it.

@mezoni

This comment has been minimized.

mezoni commented Jul 4, 2016

Possible an author want to say about the following:

type Foo interface {
  Baz() Baz
  SomeFoo() Baz
}

func (r Foo) SomeFoo() Baz {
  return r.Baz()
}

type foo struct {
  baz Baz
}

func (r *foo) Baz() Baz {
  return r.baz
}

// Abstract method, to be compatible with: interface { SomeFoo() Baz }
abstract func (r *foo) SomeFoo() Baz

func main() {
   var foo Foo
    foo = &Foo{}
    baz := foo.SomeBaz()
}
@mezoni

This comment has been minimized.

mezoni commented Jul 4, 2016

The above example is a trivial example but in a real life it can save a lot of time.
And also allows do not duplicate some code.

Eg.

type Expr interface {
  // Some members
 String() string
}

type UnaryExpr interface {
  Expr
  GetExpr() Expr
}

type PrefixExpr interface {
  UnaryExpr
  GetPrefix() string
}

// Default impl
func (r PrefixExpr) String() string {
  retrun Sprintf(%s%s, r.GetPrefix(), r.GetExpr())
}

type NotPredicateExpr struct {
}

abstract func (r *NotPredicateExpr) String() string

func (r *NotPredicateExpr) GetPrefix() string {
  return "!"
}

Currently this is impossible.
Every implementer of the type PrefixExpr interface should implement their own String() method which is not very useful.
Or programmer should use the following approach:

// Somewhere not far from `type PrefixExpr interface`
// Implementation of the `type PrefixExpr intrface { String() } `
func PrefixExpr_String(r PrefixExpr) string {
  retrun Sprintf(%s%s, r.GetPrefix(), r.GetExpr())
}

// Should take into account about the existing `PrefixExpr_String` default implementation
func (r *NotPredicateExpr) String() string {
  return PrefixExpr_String(r)
}

Which is not a very graceful.
Using an embedded structs (what I do) also does not works.

@mezoni

This comment has been minimized.

mezoni commented Jul 4, 2016

Another interesting approach:

type Foo interface {
  String()
}

// Default impl
func (r Foo) String() string {
  return "Foo"
}

type Baz interface {
  Foo
}

// Default impl
func (r Baz) String() string {
  return "Baz"
}

type baz struct {
}

func main() {
  var baz Baz
  var foo Foo
  baz :=  &baz{} 
  fmt.Prinln(baz) // => "baz"
  foo :=  &baz{} 
  fmt.Prinln(foo) // => "foo"
}

How this works?
The fmt.Println() takes an agrument as the interface {}.
The interface {} holds info about the type.

The foo := &baz{} is an type Foo interface{} value.
The type Foo interface{} has its own default implemenattion.

The baz := &baz{} is an type Baz interface{} value.
The type Baz interface{} has its own default implemenattion.

@davecheney

This comment has been minimized.

Contributor

davecheney commented Jul 4, 2016

I have no idea what this proposal is proposing. There are too many Foo's and Bar's and so on to make sense of what you are proposing.

Can you please write a piece of code that uses this feature, it does to have to compile, but it has to show how to this would be used in the real world.

Thanks

Dave

@kostya-sh

This comment has been minimized.

Contributor

kostya-sh commented Jul 4, 2016

What would fmt.Println(&baz{}) print?

In other words if a type implements two interfaces that have default
methods with the same name, which of them the type should "inherit"?
On Jul 4, 2016 15:47, "mezoni" notifications@github.com wrote:

Another interesting approach:

type Foo interface {
String()
}
// Default implfunc (r Foo) String() string {
return "Foo"
}
type Baz interface {
Foo
}
// Default implfunc (r Baz) String() string {
return "Baz"
}
type baz struct {
}
func main() {
var baz Baz
var foo Foo
baz := &baz{}
fmt.Prinln(baz) // => "baz"
foo := &baz{}
fmt.Prinln(foo) // => "foo"
}

How this works?
The fmt.Println() takes an agrument as the interface {}.
The interface {} holds info about the type.

The foo := &baz{} is an type Foo interface{} value.
The type Foo interface{} has its own default implemenattion.

The baz := &baz{} is an type Baz interface{} value.
The type Baz interface{} has its own default implemenattion.


You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
#16254 (comment), or mute
the thread
https://github.com/notifications/unsubscribe/AGy9A6qAr0hiePENb8CDRSQtqQ64pHNzks5qSMi5gaJpZM4JD1Cm
.

@mezoni

This comment has been minimized.

mezoni commented Jul 4, 2016

@davecheney

Can you please write a piece of code that uses this feature, it does to have to compile, but it has to show how to this would be used in the real world.

Do you need a very easy example for the very small real world?
Do you need a very complex example for the very big real world?

I think thet this comment explains a lot:
#16254 (comment)
It explains that currently in the Go language need to implements a spike-nail:

// Somewhere not far from `type PrefixExpr interface`
// Implementation of the `type PrefixExpr intrface { String() } `
func PrefixExpr_String(r PrefixExpr) string {
  retrun Sprintf(%s%s, r.GetPrefix(), r.GetExpr())
}

// Should take into account about the existing `PrefixExpr_String` default implementation
func (r *NotPredicateExpr) String() string {
  return PrefixExpr_String(r)
}
@davecheney

This comment has been minimized.

Contributor

davecheney commented Jul 4, 2016

Do you need a very easy example for the very small real world?
Do you need a very complex example for the very big real world

The former would be preferred.

Re. Your example, I don't understand what that is doing or why go need to change to support that.

Is this a proposal that functions can be accessed as methods on a type assuming the reciever matches the first argument of the function? D has this.

@mezoni

This comment has been minimized.

mezoni commented Jul 4, 2016

OK.
I think that this is impossible to implement similar features in the Go language only because identification of the interface performed only in the assignment expression.
After when an assignment has been made the original type remains the same.
Yes, interfaces are only interfaces.
In the Go language the values only can satisfies an interface specification but not implements them.
I was incorrect.

@dup2X

This comment has been minimized.

dup2X commented Jul 4, 2016

No need to do this.

@ulikunitz

This comment has been minimized.

Contributor

ulikunitz commented Jul 4, 2016

My understanding of the proposal is to define methods on concrete types by defining them on interfaces that the types satisfies. In the example below method Println is defined for stringer extending the method set of type A satisfying stringer. The intention seems to be to save work if a method can be implemented using other functions of a type.

package main

import (
        "fmt"
        "strconv"
)

type A int

func (a A) String() string {
        return strconv.Itoa(int(a))
}

type stringer interface {
        String() string
}

func (s stringer) Println() {
        fmt.Println(s.String())
}

func main() {
        a := A(1).
        a.Println()
}

It doesn't compile because an interface type cannot be a receiver. On top a.Println() would also not work, because it is not clear how the compiler chooses the particular interface type with Println as method if there are several interfaces satisfied by A supporting a Println method. The alternative stringer(A).Println() would solve this issue, but it doesn't extend the method set of type A which appears to be the intention. But the method could be provided explicitly in a single line of code calling the general implementation of the interface as in the code below, which compiles and works.

package main

import (
    "fmt"
    "strconv"
)

type A int

func (a A) String() string {
    return strconv.Itoa(int(a))
}

type stringer interface {
    String() string
}

func pr(s stringer) {
    fmt.Println(s.String())
}

func (a A) Println() { pr(a) }

func main() {
    a := A(1)
    a.Println()
}
@adg

This comment has been minimized.

Contributor

adg commented Jul 4, 2016

Can you please write a piece of code that uses this feature, it does to have to compile, but it has to show how to this would be used in the real world.

I second this request. Alternatively, please point to a real piece of Go code and show how it could be better written using this feature. All these tiny hypothetical examples demonstrate the what, but not the why. The "why' is critical.

@mezoni

This comment has been minimized.

mezoni commented Jul 5, 2016

@adg

I second this request. Alternatively, please point to a real piece of Go code and show how it could be better written using this feature. All these tiny hypothetical examples demonstrate the what, but not the why. The "why' is critical.

If you ask me then I already wrote that I was wrong in my judgements.
This feature is impossible to have in the Go language.

@adg

This comment has been minimized.

Contributor

adg commented Jul 5, 2016

@mezoni thanks for making that clear. My comments are addressed to anyone interested in pushing this proposal forward.

@mkideal

This comment has been minimized.

mkideal commented Jul 5, 2016

Here is an exmaple from package io:

// src/io.go
type Writer interface {
    Write(b []byte) (n int, err error)
}

type stringWriter interface {
    WriteString(s string) (n int, err error)
}

func WriteString(w Writer, s string) (n int, err error) {
    if sw, ok := w.(stringWriter); ok {
        return sw.WriteString(s)
    }
    return w.Write([]byte(s))
}

But, if we have this feature(Default implementation for interface), we can rewrite it like below:

type Writer interface {
    Write(b []byte) (n int, err error)
}

func (w Writer) WriteString(s string) (n int, err error) {
    return w.Write([]byte(s))
}

The strange interface stringWriter not needed because your implemented Writer can overides WriteString method. And using WriteString as a method of Writer maybe better than a package-level function.

@davecheney

This comment has been minimized.

Contributor

davecheney commented Jul 5, 2016

I'm sorry, I cannot see how you could write down the rules for how the
discovery of a default implementation would work. And even if they could be
described in sure this would be backwards incompatible with a significant
portion of existing go code.

I vote to close this proposal.

On Tue, 5 Jul 2016, 15:42 王仕晋 notifications@github.com wrote:

Here is an exmaple from package io:

// src/io.gotype Writer interface {
Write(b []byte) (n int, err error)
}
type stringWriter interface {
WriteString(s string) (n int, err error)
}
func WriteString(w Writer, s string) (n int, err error) {
if sw, ok := w.(stringWriter); ok {
return sw.WriteString(s)
}
return w.Write([]byte(s))
}

But, if we have this feature(Default implementation for interface), we
can rewrite like below:

type Writer interface {
Write(b []byte) (n int, err error)
}
func (w Writer) WriteString(s string) (n int, err error) {
return w.Write([]byte(s))
}

The strange interface stringWriter not needed because if your Writer can
overides WriteString method. And using WriteString as a method of Writer
better than a package-level function.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#16254 (comment), or mute
the thread
https://github.com/notifications/unsubscribe/AAAcA-KCxmX_ZbT1Qlrr2-ghhpM-TrExks5qSe7BgaJpZM4JD1Cm
.

@mkideal

This comment has been minimized.

mkideal commented Jul 5, 2016

Thanks, bye bye!

@mkideal mkideal closed this Jul 5, 2016

@golang golang locked and limited conversation to collaborators Jul 5, 2017

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.