Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Generative Programming in Go

Generative Programming in Go

monochromegane

June 21, 2015
Tweet

More Decks by monochromegane

Other Decks in Technology

Transcript

  1. ߏ଄ମʹARϥΠΫͳϝιου͕ ௥Ճ͞ΕΔ db, _ := sql.Open("sqlite3", "foo.db") Use(db) u :=

    User{Name: "test", Age: 20} u.Save() //// INSERT INTO users (name, age) VALUES (?, ?); [test 20] User{}.Where("name", "test").And("age", ">", 20).Query() //// SELECT users.id, users.name, users.age FROM users WHERE name = ? AND age > ?; [test 20]
  2. ೚ҙͷߏ଄ମʹڞ௨ͷϝιου Λ࣋ͨͤͭͭɺݸʑͷܕΛѻ͏ var u User u = User{}.First() var h

    Hoge h = Hoge{}.First() • Ωϟετͨ͘͠ͳ͍ • Genericsͷग़൪
  3. func (m User) First() (*User, error) { return m.newRelation().First() }

    func (r *UserRelation) First() (*User, error) { return r.Order("id", "ASC").Limit(1).QueryRow() } func (r *UserRelation) QueryRow() (*User, error) { row := &User{} err := r.Relation.QueryRow( row.fieldPtrsByName( r.Relation.GetColumns())...) if err != nil { return nil, err } return row, nil }
  4. see also: clipperhouse/gen // +gen slice:"All" type Person struct {

    Name string Present bool } gang := PersonSlice { {"Alice", true}, {"Bob", false}, {"Carly", true}, } here := func(p Person) bool { return p.Present } gang.All(here) // => false, Bob didn't make it
  5. ϕϯνϚʔΫ // read SomeFunc(s.F) SomeFunc(reflect.ValueOf(s).FieldByName("F").Int()) // write s.F = 100

    reflect.ValueOf(&s).Elem().FieldByName("F").SetInt(100) $ go test -benchmem -bench . • Mac OSX(10.10.3) • CPU: 2.5GHz Core i5(2Core) • Memory: 8GB • Go: 1.4.2
  6. ߏ଄ମϑΟʔϧυʹର͢ΔΞΫηε time Alloced Bytes Allocs read field 0.38 ns 0

    B 0 allocs reflect 246 ns 16 B 2 allocs write field 0.36 ns 0 B 0 allocs reflect 199 ns 8 B 1 allocs
  7. ෳ਺ͷߏ଄ମϑΟʔϧυʹର͢ΔΞΫηε time Alloced Bytes Allocs read field 0.36 ns 0

    B 0 allocs reflect 583 ns 88 B 2 allocs write field 0.37 ns 0 B 0 allocs reflect 277 ns 8 B 1 allocs
  8. func (m *User) fieldValueByName(name string) interface{} { switch name {

    case "id", "users.id": return m.Id case "name", "users.name": return m.Name case "age", "users.age": return m.Age default: return "" } } func (m *User) fieldPtrByName(name string) interface{} { switch name { case "id", "users.id": return &m.Id case "name", "users.name": return &m.Name case "age", "users.age": return &m.Age default: return nil } }
  9. see also: pquerna/ffjson //go:generate ffjson $GOFILE type Foo struct {

    Bar string } f := Foo{"bar"} b, _ := json.Marshal(f) fmt.Printf("%s\n", string(b)) // => {"Bar":"bar"} https://github.com/pquerna/ffjson
  10. ೚ҙͷߏ଄ମͷڞ௨ϝιου͸ هड़಺༰͕৑௕ʹͳΓ͕ͪ // far from the DSL... func (u *User)

    BeforeUpdate() (err error) { if u.Name != "abc"{ return fmt.Errorf("%s is invalid name.", u.Name) } if u.Age > 20 { return fmt.Errorf("%d is invalid age.", u.Age) } return nil }
  11. func (u User) validatesName() ar.Rule { return ar.MakeRule().Format().With("abc").OnCreate() } func

    (u User) validatesAge() ar.Rule { return ar.MakeRule().Numericality().OnlyInteger().LessThan(20) } rules := map[string]*ar.Validation{ "name": m.validatesName().Rule(), "age": m.validatesAge().Rule(), } for name, rule := range rules { if ok, errs := ar.NewValidator(rule).On(on).IsValid(m.fieldValueByName(name)); !ok { result = false errors.SetErrors(name, errs) } } >
  12. from := os.Getenv("GOFILE") if from == "" { if len(args)

    > 0 { from = args[0] } else { os.Exit(1) } } $ argen main.go // go:generate argen
  13. {{template "Relation" .}} {{define "Relation"}} type {{.Name}}Relation struct { src

    *{{.Name}} *ar.Relation } {{end}} func (t Template) toDefine() string { return fmt.Sprintf("{{define \"%s\"}}%s{{end}}\n", t.Name, t.Text) } var relation = &Template{ Name: "Relation", Text: ` type {{.Name}}Relation struct { src *{{.Name}} *ar.Relation }`
  14. func writeWithFormat(file, template string, structs structs) ([]byte, error) { var

    b bytes.Buffer w := bufio.NewWriter(&b) write(w, template, structs) w.Flush() formatted, err := imports.Process(file, b.Bytes(), nil) if err != nil { return nil, err } return formatted, nil } func write(w io.Writer, tplText string, structs structs) error { t := template.New("t") t.Funcs(template.FuncMap{}) tpl := template.Must(t.Parse(tplText)) if err := tpl.Execute(w, structs); err != nil { return err } return nil }