(How) can I implement a 'Stringer' abstractly based on an interface?

Issue

I am trying to implement an interface ‘abstractly’. What I mean by that is that I am trying to implement the method(s) of that interface in terms of methods of another interface.

Here’s what I tried (but it doesn’t work because it seems we are not allowed to use a Interface type as a receiver):

package symbols

import "fmt"

type Symbol interface {
    Name() string
}

type TerminalSymbol struct {
    rune rune
}

func (t TerminalSymbol) Name() string {
    return `'` + string(t.rune) + `'`
}

type NonTerminalSymbol struct {
    name string
}

func (nt NonTerminalSymbol) Name() string {
    return nt.name
}

/////////////////////////////////////////////////////////
/// Try to implement 'Stringer' interface for Symbols

func (s Symbol) String() string {
    //^ ERROR: invalid receiver type Symbol (pointer or interface type)
    return fmt.Sprintf("A symbol with name: %v", s.Name())
}

I suppose go compiler is essentially telling me that implementing one interface based on another interface is simply not supported?

I suppose I could simply take the hint and just do this instead:

func (s NonTerminalSymbol) String() string {
    return fmt.Sprintf("A symbol with name: %v", s.Name())
}

func (s TerminalSymbol) String() string {
    return fmt.Sprintf("A symbol with name: %v", s.Name())
}

That works, but it ends up duplicating the implementation of the ‘String’ method.

In Java we can easily avoid this kind of duplication; e.g. by adding concrete methods to an abstract class; or by adding a ‘default’ implementation to some of the methods in an interface.

Is there an ‘idiomatic’ way to accomplish this kind of thing in go? (i.e. can we make an abstract type like Symbol implement Stringer without duplicating the implementation of String().

Solution

You cannot have a structure similar to an abstract base class that calls the methods of the concrete implementation. There are ways to emulate that, but I am guessing that’s not what you are asking.

In Go, an interface is simply a method list, and the concept of class inheritance does not exist. You can build object hierarchies using type embedding, but that does not provide the late binding you need to implement this.

However, you can do this:

Create a stringer function for symbols:

func SymbolToString(s Symbol) string {
       return fmt.Sprintf("A symbol with name: %v", s.Name())
}

Now you can write simple implementation of String method for each struct:

type NonTerminalSymbol struct {
    name string
}

func (n NonTerminalSymbol) String() string {return SymbolToString(n)}

Answered By – Burak Serdar

Answer Checked By – Marilyn (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.