Upload
taku-fukushima
View
554
Download
6
Embed Size (px)
DESCRIPTION
These slides are for Gunosy.go #7. http://gunosygo.connpass.com/event/7643/
Citation preview
reflect&package
@takufukushima
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
Agenda• Inside(of(Interfaces
• The(Laws(of(Reflec5on
• reflect(Package
Inside'of'Interfaces
Types&and&interfaces:&Typestype MyInt intvar i intvar j MyInt// You can't assign j to i without conversion.i = j // ERROR!
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)
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
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 "???"}
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)}
itable:"an"interface"tableb := Binary(200)
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
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
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)
Memory'Op*miza*on:'Empty'interface
Memory'Op*miza*on:'Small'enough'value
Memory'Op*miza*on:'Combina*on'of'the'two'cases
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())}
The$Laws$of$Reflec.on
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
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
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.
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.
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"?
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?
"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
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}
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
reflect!Package
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)
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)
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
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
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 ...
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}
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
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
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
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
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()
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
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
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)
Value!methods:!Value!manipula0on
func (v Value) Convert(t Type) Valuefunc (v Value) Elem() Value
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))}
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"))
}
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))}
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), }}
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}
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}
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.
The$end$of$slides.$Any$ques1on?
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
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