Slide 1

Slide 1 text

Generative Programming in Go - GoCon 2015 summer - @monochromegane

Slide 2

Slide 2 text

MIYAKE Yusuke (@monochromegane)

Slide 3

Slide 3 text

GMO Pepabo, Inc.

Slide 4

Slide 4 text

♕ 3 GitHub Awards Go ranking in Japan :)

Slide 5

Slide 5 text

Generative Programming

Slide 6

Slide 6 text

ੜ੒తͳ Programming

Slide 7

Slide 7 text

Generator + Template parse generate Parser Your App Generated Code AST Code use

Slide 8

Slide 8 text

Attention, please.

Slide 9

Slide 9 text

✘ Generics Meta programming Dynamic generation

Slide 10

Slide 10 text

parse Parser AST Code Go provides

Slide 11

Slide 11 text

Generator + Template generate Your App Generated Code use We implement

Slide 12

Slide 12 text

argen ActiveRecord Generator

Slide 13

Slide 13 text

GoͰDataMapperͰ͸ͳ͘ ActiveRecordϥΠΫʹ DBૢ࡞͢Δ

Slide 14

Slide 14 text

by Generative programming

Slide 15

Slide 15 text

ߏ଄ମʹ`+AR`ϚʔΧʔΛ͚ͭΔ //+AR type User struct { Id int Name string Age int }

Slide 16

Slide 16 text

ੜ੒͢Δ $ argen main.go $ tree . !"" main.go #"" main_gen.go

Slide 17

Slide 17 text

ߏ଄ମʹ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]

Slide 18

Slide 18 text

Generative Programming ࢖͍υίϩ

Slide 19

Slide 19 text

Case.1 Generics

Slide 20

Slide 20 text

೚ҙͷߏ଄ମʹڞ௨ͷϝιου Λ࣋ͨͤͭͭɺݸʑͷܕΛѻ͏ var u User u = User{}.First() var h Hoge h = Hoge{}.First()

Slide 21

Slide 21 text

೚ҙͷߏ଄ମʹڞ௨ͷϝιου Λ࣋ͨͤͭͭɺݸʑͷܕΛѻ͏ var u User u = User{}.First() var h Hoge h = Hoge{}.First() • Ωϟετͨ͘͠ͳ͍ • Genericsͷग़൪

Slide 22

Slide 22 text

ᵆᴸᴸᴸᴸᴸᴸᴸᴸᴸᴸᴸᴸᴸᵪᴸᴸᵊ ᴺwhy no generics golangɹ ᴺݕࡧᴺ ᵎᴸᴸᴸᴸᴸᴸᴸᴸᴸᴸᴸᴸᴸᵲᴸᴸᵒ

Slide 23

Slide 23 text

Let’s generate !

Slide 24

Slide 24 text

ߏ଄ମʹରͯ͠ݸʑͷܕΛѻ͏ ڞ௨ͷϝιουΛੜ੒

Slide 25

Slide 25 text

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 }

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

Case.2 Performance

Slide 28

Slide 28 text

ϦϑϨΫγϣϯΛ࢖ͬͨ൚༻ੑ ͸ύϑΥʔϚϯεͱҾ͖׵͑ // Useful! but... var u User db.First(&u)

Slide 29

Slide 29 text

ϕϯνϚʔΫ // 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

Slide 30

Slide 30 text

ߏ଄ମϑΟʔϧυʹର͢ΔΞΫηε 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

Slide 31

Slide 31 text

ෳ਺ͷߏ଄ମϑΟʔϧυʹର͢ΔΞΫηε 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

Slide 32

Slide 32 text

Let’s generate !

Slide 33

Slide 33 text

ߏ଄ମʹରͯ͠ϑΟʔϧυ৘ใ औಘ༻ϝιουΛੜ੒

Slide 34

Slide 34 text

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 } }

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

Case.3 DSL

Slide 37

Slide 37 text

೚ҙͷߏ଄ମͷڞ௨ϝιου͸ هड़಺༰͕৑௕ʹͳΓ͕ͪ // 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 }

Slide 38

Slide 38 text

Let’s generate !

Slide 39

Slide 39 text

ϝιου໊/ίϝϯτ/λά͔Β ఆٛΛิ׬ͯ͠ੜ੒

Slide 40

Slide 40 text

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) } } >

Slide 41

Slide 41 text

Case. Bad

Slide 42

Slide 42 text

ͳΜͰ΋Generate

Slide 43

Slide 43 text

• ϝιουͷڞ௨Խɺଟଶੑͷ࣮ݱͳͲ͸ߏ଄ମͷຒࠐ (ҕৡ)΍interfaceΛ࢖ͬͯ΍Δ΂͖ • http://go-talks.appspot.com/github.com/lestrrat/ go-slides/2014-yapcasia-go-for-perl-mongers/ main.slide#27 • http://blog.monochromegane.com/blog/ 2014/03/23/struct-implementaion-patterns-in- golang/ • Generate͸࣮૷ଆ΋ར༻ଆ΋Ұख͔͔ؒΔ෼ɺෳࡶ ʹͳΓ͕ͪɻGenerateͰ΍Δཧ༝͕આ໌Ͱ͖ͳ͍ͷ Ͱ͋Ε͹࢖͏΂͖Ͱ͸ͳ͍

Slide 44

Slide 44 text

Tips

Slide 45

Slide 45 text

ίϚϯυϥΠϯ/go generate ͷ྆ํʹରԠ͢Δ

Slide 46

Slide 46 text

from := os.Getenv("GOFILE") if from == "" { if len(args) > 0 { from = args[0] } else { os.Exit(1) } } $ argen main.go // go:generate argen

Slide 47

Slide 47 text

defineΛ࢖ͬͯ templateΛ෼ׂ؅ཧ͢Δ

Slide 48

Slide 48 text

{{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 }`

Slide 49

Slide 49 text

templateͰ ࣗಈ੔ܗͱ ΦʔτΠϯϙʔτ

Slide 50

Slide 50 text

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 }

Slide 51

Slide 51 text

ςετ͸ੜ੒෺ʹ ରͯ͠ߦ͏

Slide 52

Slide 52 text

//go:generate go run ../cmd/argen/main.go package tests import "github.com/monochromegane/argen" //+AR type User struct { Name string Age int } $ go generate && go test ./...

Slide 53

Slide 53 text

એ఻ ϖύϘͰ͸ΤϯδχΞΛืू͍ͯ͠·͢ɻ ڞʹαʔϏεΛੜΈग़͠ҭͯͯ͘ΕΔ৽͍͠஥ؒ Λ଴͍ͬͯ·͢ɻ http://pepabo.com/recruit/career/engineer/

Slide 54

Slide 54 text

͓ΘΓ