Reputation: 175
I want to define an interface that has a method that returns a value whose type is the interface itself.
I tried to define the interface like this:
type Event interface {
}
type Entity interface {
ApplyEvent(command Event) (Entity, error)
}
And I would like to make a struct implement the Entity interface by doing:
type ShoppingList struct {
}
func (list ShoppingList) ApplyEvent(event Event) (ShoppingList, error) {
// code that changes "list" goes here.
return list, nil
}
If I do that and then try to pass a ShoppingList to a function that expects an Entity I get the following error:
func main() {
test(ShoppingList{})
}
func test(e Entity) {
}
Cannot use 'ShoppingList{}' (type ShoppingList) as type Entity.
Type does not implement 'Entity'
need method: ApplyEvent(command Event) (Entity, error)
have method: ApplyEvent(event Event) (ShoppingList, error)
I know I could define the interface and the receiver like this instead:
type Event interface {
}
type Entity interface {
ApplyEvent(command Event) error
}
type ShoppingList struct {
}
func (list *ShoppingList) ApplyEvent(event Event) error {
// code that changes "list" goes here.
return nil
}
But I would prefer to write the code using pure functions and immutable data structures as much as possible.
I want to return the changed value instead of mutating the receiver.
What would be the way to do it in Go?
Upvotes: 3
Views: 258
Reputation: 16302
It seems you probably already know this. But just in case you haven't thought of it yet, you could also write it like this:
type Event interface {
}
type Entity interface {
ApplyEvent(command Event) (Entity, error)
}
type ShoppingList struct {
}
func (list ShoppingList) ApplyEvent(event Event) (Entity, error) {
//...
return list
}
Here, I'm doing the same return
but I'm returning it "as" an Entity
interface rather than a ShoppingList
. If it was relevant that the Entity
was a shopping list later, I could attempt a type assertion if I wanted to see if the Entity
was a ShoppingList
later in the code.
But it would be more consistent with the interface
concept to provide an interface method for the ShoppingList
to do its thing by merit of it being an Entity rather than a consumer enumerating all possible entities. After all, why would the "Event" applied to a "ShoppingList" necessarily yield another "ShoppingLIst"? Couldn't it generate, for example, an InstacartInvoice instead? Of course, at this point I'm well beyond the scope of your question. But any time the type of an interface's concrete value is relevant to a consumer, try very hard to make it relevant to a method of that interface instead. Just like you did with ApplyEvent
.
Upvotes: 1