gunosy.go#7 reflect

53
reflect package @takufukushima

Upload: taku-fukushima

Post on 06-May-2015

554 views

Category:

Engineering


6 download

DESCRIPTION

These slides are for Gunosy.go #7. http://gunosygo.connpass.com/event/7643/

TRANSCRIPT

Page 1: Gunosy.go#7 reflect

reflect&package

@takufukushima

Page 2: Gunosy.go#7 reflect

What%is%"reflec&on"?

Reflec%on(in(compu%ng(is(the(ability(of(a(program(to(examine(its(own(structure,(par%cularly(through(types;(

it's(a(form(of(metaprogramming.(It's(also(a(great(source(of(confusion.

—"h$p://blog.golang.org/laws2of2reflec7on

Page 3: Gunosy.go#7 reflect

Agenda• Inside(of(Interfaces

• The(Laws(of(Reflec5on

• reflect(Package

Page 4: Gunosy.go#7 reflect

Inside'of'Interfaces

Page 5: Gunosy.go#7 reflect

Types&and&interfaces:&Typestype MyInt intvar i intvar j MyInt// You can't assign j to i without conversion.i = j // ERROR!

Page 6: Gunosy.go#7 reflect

Types&and&interfaces:&Interfaces

// io.Reader is the interface that wraps the// basic Read method.type Reader interface { Read(p []byte) (n int, err error)}

var r io.Readerr = os.Stdinr = bufio.NewReader(r)r = new(bytes.Buffer)

Page 7: Gunosy.go#7 reflect

Interfaces*are*sta+cally*checked

interface {}

Go's%interfaces—sta/c,%checked%at%compile%/me,%dynamic%when%asked%for—are,%for%me,%the%most%

exci/ng%part%of%Go%from%a%language%design%point%of%view.%If%I%could%export%one%feature%of%Go%into%other%

languages,%it%would%be%interfaces.

—"Russ"Cox,"Go"Data"Structures:"Interfaces

h"p://research.swtch.com/interfaces

Page 8: Gunosy.go#7 reflect

Example:)Stringer)interfacetype Stringer interface { String() string}func ToString(any interface{}) string { if v, ok := any.(Stringer); ok { return v.String() } switch v := any.(type) { case int: return strconv.Itoa(v) case float: return strconv.Ftoa(v, 'g', -1) } return "???"}

Page 9: Gunosy.go#7 reflect

Example:)Binary)has)String)method

type Binary uint64

// Binary implements Stringer interfacefunc (i Binary) String() string { return strconv.Uitob64(i.Get(), 2)}

func (i Binary) Get() uint64 { return uint64(i)}

Page 10: Gunosy.go#7 reflect

itable:"an"interface"tableb := Binary(200)

Page 11: Gunosy.go#7 reflect

Behind'the'scene'of'the'method'invoca0on

b := Binary(200)s := Stringer(b)

// var s Stringer = b makes a copy of bvar c uint64 = b // This makes a copy of b

s.String() // tab->fun[0](s.data) in C

Page 12: Gunosy.go#7 reflect

Compu&ng)the)itable

Precompu)ng,all,possible,itables,costs,a,lot.,So,they're,computed,at,run)me.,See,go/src/pkg/runtime/iface.goc.

• The%compiler%generates%a%type%descrip4on%structure%for%each%concreate%type

• The%compiler%generates%a%(different)%type%descrip4on%structure%for%each%interface%type

• The%interface%run4me%computes%the%itable%looking%for%each%method%tables

Page 13: Gunosy.go#7 reflect

Op#miza#on*of*compu#ng*the*itable• Cache'the'itable

• Sort'two'method'tables'(for'the'concrete'types'and'interface'types)'and'walking'them'simultaneously

• From'O(ni%*%nt)'to'O(ni%+%nt)

Page 14: Gunosy.go#7 reflect

Memory'Op*miza*on:'Empty'interface

Page 15: Gunosy.go#7 reflect

Memory'Op*miza*on:'Small'enough'value

Page 16: Gunosy.go#7 reflect

Memory'Op*miza*on:'Combina*on'of'the'two'cases

Page 17: Gunosy.go#7 reflect

Method'Loookup'Performance• In$Smalltalk+ish$way,$the$method$lookup$happens$every$9me$with$maybe$caching

• In$Go,$a>er$the$computa9on$of$itable$it$is$a$couple$of$memory$fetches$and$a$single$indirect$call$instruc9on

var any interface{} // initialized elsewheres := any.(Stringer) // dynamic conversionfor i := 0; i < 100; i++ { fmt.Println(s.String())}

Page 18: Gunosy.go#7 reflect

The$Laws$of$Reflec.on

Page 19: Gunosy.go#7 reflect

The$Laws$of$Reflec.on$by$Rob$Pike

h"p://blog.golang.org/laws0of0reflec5onn

1. Reflec'on*goes*from*interface*value*to*reflec'on*object

2. Reflec'on*goes*from*reflec'on*object*to*interface*value

3. To*modify*a*reflec'on*object,*the*value*must*be*se?able

Page 20: Gunosy.go#7 reflect

1.#Reflec(on#goes#from#interface#value#to#reflec(on#object

var x float64 = 3.4v := reflect.ValueOf(x)fmt.Println("type:", v.Type())fmt.Println("kind is float64:", v.Kind() == reflect.Float64)fmt.Println("value:", v.Float())

pritns

type: float64kind is float64: truevalue: 3.4

Page 21: Gunosy.go#7 reflect

Type!and!Kindvar x uint8 = 'x'v := reflect.ValueOf(x)fmt.Println("type:", v.Type()) // uint8.fmt.Println("kind is uint8: ", v.Kind() == reflect.Uint8) // true.x = uint8(v.Uint()) // v.Uint returns a uint64.

type MyInt intvar x MyInt = 7v := reflect.ValueOf(x)fmt.Println("kind is Int: ", v.Kind() == reflect.Int) // true.

Page 22: Gunosy.go#7 reflect

2.#Reflec(on#goes#from#reflec(on#object#to#interface#value

// Interface returns v's value as an interface{}.// func (v Value) Interface() interface{}y := v.Interface().(float64) // y will have type float64fmt.Println(y)fmt.Printf("value is %7.1e\n", v.Interface()) // 3.4e+00

In#short,#the#Interface#method#is#the#inverse#of#the#ValueOf#func6on,#except#that#its#result#is#always#of#

sta6c#type#interface{}.Reitera6ng:#Reflec6on#goes#from#interface#values#to#

reflec6on#objects#and#back#again.

Page 23: Gunosy.go#7 reflect

3.#To#modify#a#reflec0on#object,#the#value#must#be#se;able

var x float64 = 3.4v := reflect.ValueOf(x)v.SetFloat(7.1) // Error: will panic.// panic: reflect.Value.SetFloat using unaddressable valuevar x float64 = 3.4v := reflect.ValueOf(x)fmt.Println("settability of v:", v.CanSet())// settability of v: false

What%is%"se$ability"?

Page 24: Gunosy.go#7 reflect

Se#ability*is*determined*by*whether*the*reflec4on*object*holds*the*original*item.

var x float64 = 3.4// We're passing the copy of x here.v := reflect.ValueOf(x)// Think about the difference between f(x) and f(&x).

// v.SetFloat(7.1) updates the copied value inside the// reflection value.

So#then,#how#can#we#modify#the#reflec3on#value?

Page 25: Gunosy.go#7 reflect

"Use%the%pointer,%Luke."

var x float64 = 3.4p := reflect.ValueOf(&x) // Note: take the address of x.fmt.Println("type of p:", p.Type())// type of p: *float64fmt.Println("settability of p:", p.CanSet())// settability of p: falsev := p.Elem()fmt.Println("settability of v:", v.CanSet())// settability of v: truev.SetFloat(7.1)fmt.Println(v.Interface())// 7.1fmt.Println(x)// 7.1

Page 26: Gunosy.go#7 reflect

Modifying)Structs

type T struct { A int // Only exported fields are settable. B string}t := T{23, "skidoo"}s := reflect.ValueOf(&t).Elem()typeOfT := s.Type()for i := 0; i < s.NumField(); i++ { f := s.Field(i) fmt.Printf("%d: %s %s = %v\n", i, typeOfT.Field(i).Name, f.Type(), f.Interface())}// "0: A int = 23" and "1: B string = skidoo"s.Field(0).SetInt(77)s.Field(1).SetString("Sunset Strip")fmt.Println("t is now", t) // t is now {77 Sunset Strip}

Page 27: Gunosy.go#7 reflect

Here$again$are$the$laws$of$reflec2on:• Reflec&on)goes)from)interface)value)to)reflec&on)object

• Reflec&on)goes)from)reflec&on)object)to)interface)value

• To)modify)a)reflec&on)object,)the)value)must)be)se<able

Page 28: Gunosy.go#7 reflect

reflect!Package

Page 29: Gunosy.go#7 reflect

Constants'and'u*lity'func*ons

const ( SelectSend // case Chan <- Send SelectRecv // case <-Chan: SelectDefault // default)

func Copy(dst, src Value) int// Recursive comparison; []T{} != nilfunc DeepEqual(a1, a2 interface{}) boolfunc Select(cases []SelectCase) (chosen int, recv Value, recvOK bool)

Page 30: Gunosy.go#7 reflect

Kind

const ( Invalid Kind = iota Bool Int; Int8; Int16; Int32; Int64 Uint; Uint8; Uint16; Uint32; Uint64; Uintptr Float32; Float64 Complex64; Complex128 Array Chan Func Interface Map Ptr Slice String Struct UnsafePointer)

Page 31: Gunosy.go#7 reflect

Typestype ChanDir // A channel type's direction func (d ChanDir) String() stringtype Kind // The specific kind of type func (k Kind) String() stringtype Method // A single methodtype SelectCase // A single select casetype SelectDir // The communication directiontype SliceHeader // A slicetype StringHeader // A stringtype StructField // A single field in a structtype StructTag // The tag in a struct field func (tag StructTag) Get(key string) stringtype Type // A Go typetype Value // A Go valuetype ValueError // Error for Value func (e *ValueError) Error() string

Page 32: Gunosy.go#7 reflect

Type!related!func,ons

func ChanOf(dir ChanDir, t Type) Typefunc MapOf(key, elem Type) Typefunc PtrTo(t Type) Typefunc SliceOf(t Type) Typefunc TypeOf(i interface{}) Type

Page 33: Gunosy.go#7 reflect

Type!interfacetype Type interface { Align() int FieldAlign() int Method(int) Method MethodByName(string) (Method, bool) NumMethod() int Name() string PkgPath() string Size() uintptr String() string Kind() Kind Implements(u Type) bool // Check if it implements Type u AssignableTo(u Type) bool ConvertibleTo(u Type) bool ...

Page 34: Gunosy.go#7 reflect

Type!interface ... Bits() int ChanDir() ChanDir // for Chan IsVariadic() bool // for ...argument Elem() Type // for Array, Chan, Map, Ptr, or Slice Field(i int) StructField // for Struct FieldByIndex(index []int) StructField FieldByName(name string) (StructField, bool) FieldByNameFunc(match func(string) bool) (StructField, bool) In(i int) Type // The i'th input of the function Key() Type // for Map Len() int // for Array NumField() int // for Struct NumIn() int // The number of the input of the function NumOut() int // The number of the output of the function Out(i int) Type // The i'th output of the function}

Page 35: Gunosy.go#7 reflect

Value!related!func,onsfunc Append(s Value, x ...Value) Valuefunc AppendSlice(s, t Value) Valuefunc Indirect(v Value) Valuefunc MakeChan(typ Type, buffer int) Valuefunc MakeFunc(typ Type, fn func(args []Value) (results []Value)) Valuefunc MakeMap(typ Type) Valuefunc MakeSlice(typ Type, len, cap int) Valuefunc New(typ Type) Valuefunc NewAt(typ Type, p unsafe.Pointer) Valuefunc ValueOf(i interface{}) Valuefunc Zero(typ Type) Value

Page 36: Gunosy.go#7 reflect

Value!methods:!Type!and!kindfunc (v Value) Addr() Valuefunc (v Value) Bool() boolfunc (v Value) Bytes() []bytefunc (v Value) Complex() complex128func (v Value) Float() float64func (v Value) Int() int64func (v Value) Interface() (i interface{})func (v Value) InterfaceData() [2]uintptrfunc (v Value) Kind() Kindfunc (v Value) Pointer() uintptrfunc (v Value) Slice(i, j int) Valuefunc (v Value) Slice3(i, j, k int) Valuefunc (v Value) String() stringfunc (v Value) Type() Typefunc (v Value) Uint() uint64func (v Value) UnsafeAddr() uintptr

Page 37: Gunosy.go#7 reflect

Value!methods:!Can_,!Is_!and!Overflow_

func (v Value) CanAddr() boolfunc (v Value) CanInterface() boolfunc (v Value) CanSet() bool

func (v Value) IsNil() boolfunc (v Value) IsValid() bool

func (v Value) OverflowComplex(x complex128) boolfunc (v Value) OverflowFloat(x float64) boolfunc (v Value) OverflowInt(x int64) boolfunc (v Value) OverflowUint(x uint64) bool

Page 38: Gunosy.go#7 reflect

Value!methods:!Array,!Slice!and!Map

func (v Value) Index(i int) Valuefunc (v Value) Cap() intfunc (v Value) Len() int

func (v Value) MapIndex(key Value) Valuefunc (v Value) MapKeys() []Value

Page 39: Gunosy.go#7 reflect

Value!methods:!Channel

func (v Value) Recv() (x Value, ok bool)func (v Value) Send(x Value)func (v Value) TryRecv() (x Value, ok bool)func (v Value) TrySend(x Value) boolfunc (v Value) Close()

Page 40: Gunosy.go#7 reflect

Value!methods:!Func.on!and!method

func (v Value) Call(in []Value) []Valuefunc (v Value) CallSlice(in []Value) []Valuefunc (v Value) Method(i int) Valuefunc (v Value) MethodByName(name string) Valuefunc (v Value) NumMethod() int

Page 41: Gunosy.go#7 reflect

Value!methods:!Field!of!structfunc (v Value) Field(i int) Valuefunc (v Value) FieldByIndex(index []int) Valuefunc (v Value) FieldByName(name string) Valuefunc (v Value) FieldByNameFunc(match func(string) bool) Valuefunc (v Value) NumField() int

Page 42: Gunosy.go#7 reflect

Value!methods:!Se+ersfunc (v Value) Set(x Value)func (v Value) SetBool(x bool)func (v Value) SetBytes(x []byte)func (v Value) SetCap(n int)func (v Value) SetComplex(x complex128)func (v Value) SetFloat(x float64)func (v Value) SetInt(x int64)func (v Value) SetLen(n int)func (v Value) SetMapIndex(key, val Value)func (v Value) SetPointer(x unsafe.Pointer)func (v Value) SetString(x string)func (v Value) SetUint(x uint64)

Page 43: Gunosy.go#7 reflect

Value!methods:!Value!manipula0on

func (v Value) Convert(t Type) Valuefunc (v Value) Elem() Value

Page 44: Gunosy.go#7 reflect

Example:)MakeFunc...func main() { swap := func(in []reflect.Value) []reflect.Value { return []reflect.Value{in[1], in[0]} } makeSwap := func(fptr interface{}) { fn := reflect.ValueOf(fptr).Elem() v := reflect.MakeFunc(fn.Type(), swap) fn.Set(v) } var intSwap func(int, int) (int, int) makeSwap(&intSwap) fmt.Println(intSwap(0, 1))

var floatSwap func(float64, float64) (float64, float64) makeSwap(&floatSwap) fmt.Println(floatSwap(2.72, 3.14))}

Page 45: Gunosy.go#7 reflect

Example:)StructTagpackage main

import ( "fmt" "reflect")

func main() { type S struct { F string `species:"gopher" color:"blue"` }

s := S{} st := reflect.TypeOf(s) field := st.Field(0) fmt.Println(field.Tag.Get("color"), field.Tag.Get("species"))

}

Page 46: Gunosy.go#7 reflect

Example:)go-martini/martini// Run the http server. Listening on os.GetEnv("PORT") or// 3000 by default.func (m *Martini) Run() { port := os.Getenv("PORT") if port == "" { port = "3000" }

host := os.Getenv("HOST") // Dependency injection here. logger := m.Injector.Get( reflect.TypeOf(m.logger)).Interface().(*log.Logger)

logger.Printf("listening on %s:%s (%s)\n", host, port, Env) logger.Fatalln(http.ListenAndServe(host+":"+port, m))}

Page 47: Gunosy.go#7 reflect

Example:)codegangsta/inject

Dependency(Injec+on((DI)(in(Go

type injector struct { values map[reflect.Type]reflect.Value parent Injector}

// New returns a new Injector.func New() Injector { return &injector{ values: make(map[reflect.Type]reflect.Value), }}

Page 48: Gunosy.go#7 reflect

Example:)codegangsta/injectfunc (i *injector) Map(val interface{}) TypeMapper { i.values[reflect.TypeOf(val)] = reflect.ValueOf(val) return i}func (i *injector) MapTo(val interface{}, ifacePtr interface{}) TypeMapper { i.values[InterfaceOf(ifacePtr)] = reflect.ValueOf(val) return i}func (i *injector) Set(typ reflect.Type, val reflect.Value) TypeMapper { i.values[typ] = val return i}

Page 49: Gunosy.go#7 reflect

Example:)codegangsta/injectfunc (i *injector) Get(t reflect.Type) reflect.Value { val := i.values[t] if val.IsValid() { return val } if t.Kind() == reflect.Interface { for k, v := range i.values { if k.Implements(t) { val = v break } } } if !val.IsValid() && i.parent != nil { val = i.parent.Get(t) } return val}

Page 50: Gunosy.go#7 reflect

Use$Negroni,$not$Mar/ni

Mar$ni'reflec$on'is'flawed;!"The!real!tradeoff!with!reflec1on!is!one!of!complexity."

—"My"Thoughts"on"Mar/ni,"Code"Gangsta

h"p://blog.codegangsta.io/blog/2014/05/19/my;thoughts;on;mar>ni/

Gin$is$also$an$alterna,ve.

Page 51: Gunosy.go#7 reflect

The$end$of$slides.$Any$ques1on?

Page 52: Gunosy.go#7 reflect

References• The%Go%Blog:%The%Laws%of%Reflec4on

• h6p://blog.golang.org/laws<of<reflec4on

• Go%Data%Structures

• Interfaces%h6p://research.swtch.com/interfaces

• Package%reflect

• h6p://golang.org/pkg/reflect/

• go<mar4ni/mar4ni

• h6ps://github.com/go<mar4ni/mar4ni

Page 53: Gunosy.go#7 reflect

References• codegangsta/inject

• h/ps://github.com/codegangsta/inject

• My8Thoughts8on8Mar;ni,8Code8Gangsta

• h/p://blog.codegangsta.io/blog/2014/05/19/myFthoughtsFonFmar;ni/

• ginFgonic/gin

• h/ps://github.com/ginFgonic/gin