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. Generative
    Programming
    in Go
    - GoCon 2015 summer -
    @monochromegane

    View Slide

  2. MIYAKE Yusuke
    (@monochromegane)

    View Slide

  3. GMO Pepabo, Inc.

    View Slide


  4. 3
    GitHub Awards
    Go ranking in Japan :)

    View Slide

  5. Generative
    Programming

    View Slide

  6. ੜ੒తͳ
    Programming

    View Slide

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

    View Slide

  8. Attention, please.

    View Slide


  9. Generics
    Meta programming
    Dynamic generation

    View Slide

  10. parse
    Parser
    AST
    Code
    Go
    provides

    View Slide

  11. Generator
    +
    Template
    generate
    Your
    App
    Generated
    Code
    use
    We
    implement

    View Slide

  12. argen
    ActiveRecord Generator

    View Slide

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

    View Slide

  14. by
    Generative
    programming

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  18. Generative
    Programming
    ࢖͍υίϩ

    View Slide

  19. Case.1
    Generics

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  23. Let’s generate !

    View Slide

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

    View Slide

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

    View Slide

  26. 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

    View Slide

  27. Case.2
    Performance

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  32. Let’s generate !

    View Slide

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

    View Slide

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

    View Slide

  35. 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

    View Slide

  36. Case.3
    DSL

    View Slide

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

    View Slide

  38. Let’s generate !

    View Slide

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

    View Slide

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

    View Slide

  41. Case. Bad

    View Slide

  42. ͳΜͰ΋Generate

    View Slide

  43. • ϝιουͷڞ௨Խɺଟଶੑͷ࣮ݱͳͲ͸ߏ଄ମͷຒࠐ
    (ҕৡ)΍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Ͱ΍Δཧ༝͕આ໌Ͱ͖ͳ͍ͷ
    Ͱ͋Ε͹࢖͏΂͖Ͱ͸ͳ͍

    View Slide

  44. Tips

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  52. //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 ./...

    View Slide

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

    View Slide

  54. ͓ΘΓ

    View Slide