Hi Ian , I've modified the example towards a more specific use case. The main idea in the example below is to make code related to database operations(i.e SELECT queries) safer and easier to read. A kind of json.Unmarshal/Marshal for databases, with validation (type checking, param numbers etc) to avoid a class of bugs/errors such invalid param types/numbers passed, invalid queries, invalid resource type to scan into etc. Currently the function returned by *Select* throws the validation errors at runtime (i.e. invalid param type passed etc). It would be great to have that class of errors checked at compile type. The only way I could achieve that kind of type checking was through code generation. I already built a tool to generate functions with the proper param types but it seems that code generation introduces a lot of friction to the point that I stopped using it.
My hope is that one day a Go feature (i.e. a version of Generics) could help the function returned by *func* *Select* be type checked at compile time. https://play.golang.org/p/-Th7aHDGORL package main import ( "database/sql" "errors" "fmt" "reflect" ) var sqlDB *sql.DB type Flower struct { Color string Size int Weight int } type T struct{} var FlowerByColor = Select(" * FROM tablex WHERE Color=$ LIMIT 1", reflect.TypeOf(Flower{})) func main() { // Select a flower based on its color from database // invalid resource type; resource of type Flower is expected // I would like this to not compile as I pass an invalid resource type T instead of type Flower var color string if err := FlowerByColor(T{}, &color); err != nil { fmt.Printf("err %v\n", err) } // invalid param; type string (Folower.Color) type is expected var colorInvalid int // I would like this to not compile as I pass an invalid color type if err := FlowerByColor(Flower{}, colorInvalid); err != nil { fmt.Printf("err %v\n", err) } // correct query color = "red" if err := FlowerByColor(Flower{}, color); err != nil { fmt.Printf("err %v\n", err) } // Note: a proper SelectFunc would actually accept only pointers to Flower so that // it can unmarshal data into the resource as below. For brevity I omitted handling // pointers in SelectFunc resource := new(Flower) if err := FlowerByColor(resource, color); err != nil { fmt.Printf("err %v\n", err) } // so something with the data from realVal fmt.Printf("our flower of color %v has a size of %v", color, realVal.Size) } // SelectFunc receives a resource and the query params type SelectFunc func(resource interface{}, params ...interface{}) error // select receives a sql query and the type that represents //the sql table from database. // Returns a function that executes the sql query with the matching params from tv. func Select(q string, tv reflect.Type) SelectFunc { paramTypes, err := parseQuery(q, tv) if err != nil { panic("invalid query") } return func(resource interface{}, param ...interface{}) error { // validate input // resource must match the resource type if reflect.TypeOf(resource) != tv { return errors.New("invalid resource type") } if len(param) != len(paramTypes) { return errors.New("invalid number of params passed") } for k, v := range param { if reflect.TypeOf(v) != paramTypes[k] { return errors.New("invalid argv type passed") } } // do a select database query resourceFields := fieldsFromResource(reflect.ValueOf(resource)) if err := sqlDB.QueryRow("SELECT "+q, param...).Scan(resourceFields...); err != nil { return err } return nil } } // parseQuery parses query and the resource t. // returns the types selected in the query func parseQuery(query string, t reflect.Type) ([]reflect.Type, error) { // skip parsing for brevity return []reflect.Type{t.Field(0).Type}, nil } func fieldsFromResource(v reflect.Value) []interface{} { // skip type fields looping for brevity return []interface{}{ v.Field(0).Addr().Interface(), v.Field(1).Addr().Interface(), v.Field(2).Addr().Interface(), } } On Wednesday, October 6, 2021 at 2:14:20 AM UTC+3 Ian Lance Taylor wrote: > On Sun, Oct 3, 2021 at 8:41 AM mi...@ubo.ro <mi...@ubo.ro> wrote: > > > > I have developed a library that depends very much on reflect package. It > caches a specific type and return a function that encodes(does something > with) with that kind /type of data. Think of defining database schema using > types and generating functions to validate/update/insert data. > > > > I reduced it to a basic example below. Can the generics feature from Go > help me make it any safer? Ideally I would like to make the program below > not to compile due the errors of invalid params passed to PrintT instead to > throw dynamically at runtime. > > I don't understand what your program is trying to do, but the current > generics proposal does not support variadic generic parameters, so I > don't think it's going to help you. > > 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+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/28bc1402-c789-43ca-ae24-791ca93d541bn%40googlegroups.com.