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 full-size slide

  2. MIYAKE Yusuke
    (@monochromegane)

    View full-size slide

  3. GMO Pepabo, Inc.

    View full-size slide


  4. 3
    GitHub Awards
    Go ranking in Japan :)

    View full-size slide

  5. Generative
    Programming

    View full-size slide

  6. ੜ੒తͳ
    Programming

    View full-size slide

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

    View full-size slide

  8. Attention, please.

    View full-size slide


  9. Generics
    Meta programming
    Dynamic generation

    View full-size slide

  10. parse
    Parser
    AST
    Code
    Go
    provides

    View full-size slide

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

    View full-size slide

  12. argen
    ActiveRecord Generator

    View full-size slide

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

    View full-size slide

  14. by
    Generative
    programming

    View full-size slide

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

    View full-size slide

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

    View full-size 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 full-size slide

  18. Generative
    Programming
    ࢖͍υίϩ

    View full-size slide

  19. Case.1
    Generics

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  23. Let’s generate !

    View full-size slide

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

    View full-size 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 full-size 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 full-size slide

  27. Case.2
    Performance

    View full-size slide

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

    View full-size 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 full-size 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 full-size 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 full-size slide

  32. Let’s generate !

    View full-size slide

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

    View full-size 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 full-size 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 full-size slide

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

  37. Let’s generate !

    View full-size slide

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

    View full-size slide

  39. 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 full-size slide

  40. ͳΜͰ΋Generate

    View full-size slide

  41. • ϝιουͷڞ௨Խɺଟଶੑͷ࣮ݱͳͲ͸ߏ଄ମͷຒࠐ
    (ҕৡ)΍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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  45. {{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 full-size slide

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

    View full-size slide

  47. 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 full-size slide

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

    View full-size slide

  49. //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 full-size slide

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

    View full-size slide