Attempting to Implement the Visitor Pattern in Go using Generics - go

I have the following simple generics based go package that implements the GoF Visitor pattern:
package patterns
type Social interface {
AcceptVisitor(visitor *Visitor)
}
type Component struct {
}
func (c *Component) AcceptVisitor(visitor *Visitor) {
visitor.VisitComponent(c)
}
type Collection[T Social] struct {
Component
items[]T
}
func (c *Collection[T]) AcceptVisitor(visitor *Visitor) {
visitor.VisitCollection(c) // <- Error Here
}
type Visitor struct {
}
func (v *Visitor) VisitComponent(component *Component) {
}
func (v *Visitor) VisitCollection(collection *Collection[Social]) {
for _, item := range collection.items {
item.AcceptVisitor(v)
}
}
The compiler gives the following error:
./patterns.go:20:26: cannot use c (variable of type *Collection[T]) as
type *Collection[Social] in argument to visitor.VisitCollection
This seems strange to me since the generic type T is constrained as Social.
I tried a couple things:
Replaced the Visitor abstract type with an interface definition. This
resulted in circular dependencies between the Social and Visitor
interfaces.
Removed the generics from the declarations which fixes the problem
but we pretty much need generics for the Collection type.
It seems like go should be able to handle the generics in this code.
POSSIBLE SOLUTION:
After a really helpful discussion with #blackgreen we decided that the problem shows up due to a few things:
Go, being (truly) strictly typed, does not allow an argument that is being passed into a function to be "narrowed" to a subset of the original type even though the compiler could still prove it to be safe. Whether or not Go should allow narrowing is up for debate.
Go does not allow generic constraints on a method since the constraints might conflict with generic constraints on the structure associated with the method.
Go, rightly so, does not allow circular dependencies. We could abstract all the dependencies for the Visitor pattern into interfaces but would then have the circular dependencies required by the "double dispatch" aspect of the pattern.
To work-around these items, and still get the benefits of the Visitor pattern, we can change the VisitXYZ() methods in the Visitor structure to be (potentially generic) functions that each take a *Visitor argument as the first parameter of the function and the object being visited as the second parameter.
I posted this solution in the Go Playground: https://go.dev/play/p/vV7v61teFbj
NOTE: Even though this possible solution does appear to solve the problem, it really doesn't. If you think about writing several different types of Visitors (one for pretty printing, one for copying, one for sorting, etc.) you quickly realize that since the VisitXYZ() functions are not methods, you cannot have multiple versions of each function for each Visitor type. In the end, the fact that the Visitor pattern really does require a circular dependency between the Social interface and the Visitor interface dooms it for Go. I am closing this post but will leave the analysis so that others won't need to repeat it.

I came to the conclusion that generics make this pattern worse. By parametrizing the Collection struct, you force items []T to have the same elements. With plain interfaces instead you can have dynamic dispatch, hence allow items to contain different implementations. This alone should be sufficient reason.
This is a minimal implementation without generics, adapted from some Java examples (runnable code):
Main interfaces:
type Visitor func(Element)
type Element interface {
Accept(Visitor)
}
An implementor:
type Foo struct{}
func (f Foo) Accept(visitor Visitor) {
visitor(f)
}
The container:
type List struct {
elements []Element
}
func (l *List) Accept(visitor Visitor) {
for _, e := range l.elements {
e.Accept(visitor)
}
visitor(l)
}
The visitor itself, as a regular function. And here you can define any function at all freely. Being untyped, you can pass it directly as a Visitor argument:
func doVisitor(v Element) {
switch v.(type) {
case *List:
fmt.Println("visiting list")
case Foo:
fmt.Println("visiting foo")
case Bar:
fmt.Println("visiting bar")
}
}

Related

Golang wrap calls to package methods generically [duplicate]

I want to write a single function that can add certain fields to Firebase message structs. There are two different types of message, Message and MulticastMessage, which both contain Android and APNS fields of the same types, but the message types don't have an explicitly declared relationship with each other.
I thought I should be able to do this:
type firebaseMessage interface {
*messaging.Message | *messaging.MulticastMessage
}
func highPriority[T firebaseMessage](message T) T {
message.Android = &messaging.AndroidConfig{...}
....
return message
}
but it gives the error message.Android undefined (type T has no field or method Android). And I can't write switch m := message.(type) either (cannot use type switch on type parameter value message (variable of type T constrained by firebaseMessage)).
I can write switch m := any(message).(type), but I'm still not sure whether that will do what I want.
I've found a few other SO questions from people confused by unions and type constraints, but I couldn't see any answers that helped explain why this doesn't work (perhaps because I'm trying to use it with structs instead of interfaces?) or what union type constraints are actually useful for.
In Go 1.18 you cannot access common fields1, nor common methods2, of type parameters. Those features don't work simply because they are not yet available in the language. As shown in the linked threads, the common solution is to specify methods to the interface constraint.
However the the types *messaging.Message and *messaging.MulticastMessage do not have common accessor methods and are declared in a library package that is outside your control.
Solution 1: type switch
This works fine if you have a small number of types in the union.
func highPriority[T firebaseMessage](message T) T {
switch m := any(message).(type) {
case *messaging.Message:
setConfig(m.Android)
case *messaging.MulticastMessage:
setConfig(m.Android)
}
return message
}
func setConfig(cfg *messaging.AndroidConfig) {
// just assuming the config is always non-nil
*cfg = &messaging.AndroidConfig{}
}
Playground: https://go.dev/play/p/9iG0eSep6Qo
Solution 2: wrapper with method
This boils down to How to add new methods to an existing type in Go? and then adding that method to the constraint. It's still less than ideal if you have many structs, but code generation may help:
type wrappedMessage interface {
*MessageWrapper | *MultiCastMessageWrapper
SetConfig(c foo.Config)
}
type MessageWrapper struct {
messaging.Message
}
func (w *MessageWrapper) SetConfig(cfg messaging.Android) {
*w.Android = cfg
}
// same for MulticastMessageWrapper
func highPriority[T wrappedMessage](message T) T {
// now you can call this common method
message.SetConfig(messaging.Android{"some-value"})
return message
}
Playground: https://go.dev/play/p/JUHp9Fu27Yt
Solution 3: reflection
If you have many structs, you're probably better off with reflection. In this case type parameters are not strictly needed but help provide additional type safety. Note that the structs and fields must be addressable for this to work.
func highPriority[T firebaseMessage](message T) T {
cfg := &messaging.Android{}
reflect.ValueOf(message).Elem().FieldByName("Android").Set(reflect.ValueOf(cfg))
return message
}
Playground: https://go.dev/play/p/3DbIADhiWdO
Notes:
How can I define a struct field in my interface as a type constraint (type T has no field or method)?
In Go generics, how to use a common method for types in a union constraint?

Unioning an interface with a type in golang

I'm trying to implement some caching functions in Golang but I want them to be valid for both strings and other objects that implement the Stringer interface. I'm making an attempt of it using Golang generics and this is what I have so far:
import (
"fmt"
)
type String interface {
~string | fmt.Stringer
}
However, this gives an error cannot use fmt.Stringer in union (fmt.Stringer contains methods). Is there a way to do this without relying on reflection or type boxing/unboxing?
The confusion might be warranted because the type parameters proposal suggests code like yours, however ended up as an implementation restriction in Go 1.18.
It is mentioned in the specs, and in Go 1.18 release notes. The specs are the normative reference:
Implementation restriction: A union (with more than one term) cannot contain the predeclared identifier comparable or interfaces that specify methods, or embed comparable or interfaces that specify methods.
There is also a somewhat extensive explanation of why this wasn't included in Go 1.18 release. The tl;dr is simplifying the computation of union type sets (although in Go 1.18 method sets of type parameters aren't computed implicitly either...).
Consider also that with or without this restriction you likely wouldn't gain anything useful, beside passing T to functions that use reflection. To call methods on ~string | fmt.Stringer you still have to type-assert or type-switch.
Note that if the purpose of such constraint is simply to print the string value, you can just use fmt.Sprint, which uses reflection.
For the broader case, type assertion or switch as in colm.anseo's answer works just fine when the argument can take exact types as string (without ~) and fmt.Stringer. For approximations like ~string you can't exhaustively handle all possible terms, because those type sets are virtually infinite. So you're back to reflection. A better implementation might be:
func StringLike(v any) string {
// switch exact types first
switch s := v.(type) {
case fmt.Stringer:
return s.String()
case string:
return s
}
// handle the remaining type set of ~string
if r := reflect.ValueOf(v); r.Kind() == reflect.String {
return r.String()
}
panic("invalid type")
}
Playground: https://go.dev/play/p/-wzo2KPKzWZ
Generics - which allows in theory many types to be used - settles on a single concrete type at compilation time. Interfaces allow for multiple types at runtime. You are looking to combine both of these at once -unfortunately that is not possible.
The closest you can get without using reflection would be using a runtime type assertion:
func StringLike(v any) string {
if s, ok := v.(string); ok {
return s
}
if s, ok := v.(fmt.Stringer); ok {
return s.String()
}
panic("non string invalid type")
}
https://go.dev/play/p/p4QHuT6R8yO

Store a collection of constructors for types that all conform to the same interface

I'm making an app that'll need sets of rules to run a job. The app offers the possibility to express the rules in one of several different languages. I therefore have defined an interface to a live rules engine, that offers the methods that the app will need to query the current set of rules. Behind this interface, there will be one different type of engine, according to the source language.
Now I'd like to instantiate a rules engine according to the rule file's extension. But I get some errors which I have a hard time to overcome.
Let me first offer this simplified skeleton :
package main
//
//
// The interface
type RulesEngine interface {
SomeRuleEvaluator(string) bool
}
//
//
// An implementation, with its constructor
type ASimpleRulesEngine struct {
// I've also tried with :
// RulesEngine
}
func NewASimpleRulesEngine(context string) *ASimpleRulesEngine {
re := ASimpleRulesEngine{}
return &re
}
func (re *ASimpleRulesEngine) SomeRuleEvaluator(dummy string) bool {
return true
}
//
//
// A client, that'll want to be able to choose a constructor
var rulesEngineConstructorsPerExtension map[string](func(string) RulesEngine)
func init() {
rulesEngineConstructorsPerExtension = make(map[string](func(string)RulesEngine))
rulesEngineConstructorsPerExtension[".ini"] = NewASimpleRulesEngine
}
func main() {
}
When I try to build this, I get 35: cannot use NewASimpleRulesEngine (type func(string) *ASimpleRulesEngine) as type func(string) RulesEngine in assignment
I've also tried :
assigning without a pointer, although I felt stupid while trying it
having an intermediate step in the initfunction, where I'd create a new(func (string) RulesEngine) and then assign to it, with and without pointer.
storing function pointers like in C, but the compiler said it could not take the adress of my function.
I'm not that familiar with Go and this felt a bit surprising. What would be the proper type signature to use ? Is this possible at all ? If it's unavoidable, I'll obviously have a simple array of extensions on one side (to check if a file is potentially a rules file), and a big switch on the other side to provide the adequate constructor, but as much possible I'd like to avoid such duplication.
Thank you for any insight !
(edited : I've accepted my own answer for lack of any other, but its most relevant part is #seh 's comment below)
Following #JorgeMarey 's comment, and not wanting to sacrifice the constructor's type signature, I came up with this. But it does feel very tacky to me. I'll be glad to hear about a cleaner way.
func init() {
rulesEngineConstructorsPerExtension = make(map[string](func(string)RulesEngine))
cast_NewASimpleRulesEngine := func(content string) RulesEngine {
return NewASimpleRulesEngine(content)
}
rulesEngineConstructorsPerExtension[".ini"] = cast_NewASimpleRulesEngine
}
(an attempt to explicitly cast with (func(string)RulesEngine)( NewASimpleRulesEngine) was deemed unfit by the compiler too)

Best practice for unions in Go

Go has no unions. But unions are necessary in many places. XML makes excessive use of unions or choice types. I tried to find out, which is the preferred way to work around the missing unions. As an example I tried to write Go code for the non terminal Misc in the XML standard which can be either a comment, a processing instruction or white space.
Writing code for the three base types is quite simple. They map to character arrays and a struct.
type Comment Chars
type ProcessingInstruction struct {
Target *Chars
Data *Chars
}
type WhiteSpace Chars
But when I finished the code for the union, it got quite bloated with many redundant functions. Obviously there must be a container struct.
type Misc struct {
value interface {}
}
In order to make sure that the container holds only the three allowed types I made the value private and I had to write for each type a constructor.
func MiscComment(c *Comment) *Misc {
return &Misc{c}
}
func MiscProcessingInstruction (pi *ProcessingInstruction) *Misc {
return &Misc{pi}
}
func MiscWhiteSpace (ws *WhiteSpace) *Misc {
return &Misc{ws}
}
In order to be able to test the contents of the union it was necessary to write three predicates:
func (m Misc) IsComment () bool {
_, itis := m.value.(*Comment)
return itis
}
func (m Misc) IsProcessingInstruction () bool {
_, itis := m.value.(*ProcessingInstruction)
return itis
}
func (m Misc) IsWhiteSpace () bool {
_, itis := m.value.(*WhiteSpace)
return itis
}
And in order to get the correctly typed elements it was necessary to write three getters.
func (m Misc) Comment () *Comment {
return m.value.(*Comment)
}
func (m Misc) ProcessingInstruction () *ProcessingInstruction {
return m.value.(*ProcessingInstruction)
}
func (m Misc) WhiteSpace () *WhiteSpace {
return m.value.(*WhiteSpace)
}
After this I was able to create an array of Misc types and use it:
func main () {
miscs := []*Misc{
MiscComment((*Comment)(NewChars("comment"))),
MiscProcessingInstruction(&ProcessingInstruction{
NewChars("target"),
NewChars("data")}),
MiscWhiteSpace((*WhiteSpace)(NewChars(" \n")))}
for _, misc := range miscs {
if (misc.IsComment()) {
fmt.Println ((*Chars)(misc.Comment()))
} else if (misc.IsProcessingInstruction()) {
fmt.Println (*misc.ProcessingInstruction())
} else if (misc.IsWhiteSpace()) {
fmt.Println ((*Chars)(misc.WhiteSpace()))
} else {
panic ("invalid misc");
}
}
}
You see there is much code looking almost the same. And it will be the same for any other union. So my question is: Is there any way to simplify the way to deal with unions in Go?
Go claims to simplify programing work by removing redundancy. But I think the above example shows the exact opposite. Did I miss anything?
Here is the complete example: http://play.golang.org/p/Zv8rYX-aFr
As it seems that you're asking because you want type safety, I would firstly argue that your initial
solution is already unsafe as you have
func (m Misc) Comment () *Comment {
return m.value.(*Comment)
}
which will panic if you haven't checked IsComment before. Therefore this solution has no benefits over
a type switch as proposed by Volker.
Since you want to group your code you could write a function that determines what a Misc element is:
func IsMisc(v {}interface) bool {
switch v.(type) {
case Comment: return true
// ...
}
}
That, however, would bring you no compiler type checking either.
If you want to be able to identify something as Misc by the compiler then you should
consider creating an interface that marks something as Misc:
type Misc interface {
ImplementsMisc()
}
type Comment Chars
func (c Comment) ImplementsMisc() {}
type ProcessingInstruction
func (p ProcessingInstruction) ImplementsMisc() {}
This way you could write functions that are only handling misc. objects and get decide later
what you really want to handle (Comments, instructions, ...) in these functions.
If you want to mimic unions then the way you wrote it is the way to go as far as I know.
I think this amount of code might be reduced, e.g. I personally do not think that safeguarding type Misc against containing "illegal" stuff is really helpful: A simple type Misc interface{} would do, or?
With that you spare the constructors and all the Is{Comment,ProcessingInstruction,WhiteSpace} methods boil down to a type switch
switch m := misc.(type) {
Comment: fmt.Println(m)
...
default: panic()
}
Thats what package encoding/xml does with Token.
I am not sure to understand your issue. The 'easy' way to do it would be like the encoding/xml package with interface{}. If you do not want to use interfaces, then you can do something like you did.
However, as you stated, Go is a typed language and therefore should be use for typed needs.
If you have a structured XML, Go can be a good fit, but you need to write your schema. If you want a variadic schema (one given field can have multiple types), then you might be better off with an non-typed language.
Very useful tool for json that could easily rewritten for xml:
http://mholt.github.io/json-to-go/
You give a json input and it gives you the exact Go struct. You can have multiple types, but you need to know what field has what type. If you don't, you need to use the reflection and indeed you loose a lot of the interest of Go.
TL;DR You don't need a union, interface{} solves this better.
Unions in C are used to access special memory/hardware. They also subvert the type system. Go does not have the language primitives access special memory/hardware, it also shunned volatile and bit-fields for the same reason.
In C/C++ unions can also be used for really low level optimization / bit packing. The trade off: sacrifice the type system and increase complexity in favor of saving some bits. This of course comes with all the warnings about optimizations.
Imagine Go had a native union type. How would the code be better? Rewrite the code with this:
// pretend this struct was a union
type MiscUnion struct {
c *Comment
pi *ProcessingInstruction
ws *WhiteSpace
}
Even with a builtin union accessing the members of MiscUnion requires a runtime check of some kind. So using an interface is no worse off. Arguably the interface is superior as the runtime type checking is builtin (impossible to get wrong) and has really nice syntax for dealing with it.
One advantage of a union type is static type check to make sure only proper concrete types where put in a Misc. The Go way of solving this is "New..." functions, e.g. MiscComment, MiscProcessingInstruction, MiscWhiteSpace.
Here is a cleaned up example using interface{} and New* functions: http://play.golang.org/p/d5bC8mZAB_

Can embedded methods access "parent" fields?

Background
I've done a fair amount of spec reading and code testing and I think the answer is no, but I want to make sure I'm not missing anything.
Goal
Basically, I'm trying to create a Active Record style ORM for Go, because I like how readable it is and how abstracted it is from its back end data store. I'd rather write user.Save() than data.Save(user) by embedding common CRUD methods on the user struct.
Example
package main
import (
"fmt"
"reflect"
)
func main() {
test := Foo{Bar: &Bar{}, Name: "name"}
test.Test()
}
type Foo struct {
*Bar
Name string
}
func (s *Foo) Method() {
fmt.Println("Foo.Method()")
}
type Bar struct {
}
func (s *Bar) Test() {
t := reflect.TypeOf(s)
v := reflect.ValueOf(s)
fmt.Printf("model: %+v %+v %+v\n", s, t, v)
fmt.Println(s.Name)
s.Method()
}
http://play.golang.org/p/cWyqqVSKGH
Question
Is there a way to make top-level fields (not sure what the correct term in Go is for these) accessible from embedded methods (eg: s.Name or s.Method()?
Thank you donating your time to a new Gopher.
Go doesn't provide any support for what you're after: the receiver of your Test method is a Bar pointer, and there is no way to tell whether it is embedded or not.
If you really want to go this route, one option would be to add an interface{} member to Bar and require that types that it be set to the containing type. Initialising this member could either be the responsibility of whoever created the value, or perhaps require callers to pass the value to some ORM method to set it. This isn't particularly pretty, but it's probably the best you can do.
With that out of the way, is it really that bad to structure the API as db.Save(user) rather than user.Save()? The former offers an obvious way to extend to multiple databases, while the latter seems more likely to rely on global state.
(If I understood your question correctly,) no, embedding isn't inheritance. It sounds like what you're actually after is an interface
type Saver interface {
Save() error
}
then the relevant parties can implement that.
You can have a common struct base or whatever that implements common methods and then each higher-level struct can embed base to allow them to share implementation.

Resources