Share
Embed
Go for Javaneros
An overview of the Go programming language from the point of view of a seasoned Java developer.
golang, java
Francesc Campoy Flores
September 12, 2014
Transcript
66 slides
-
What is Go? Go is an open-source programming language created
at Google, to solve Google-scale problems. -
Who uses Go? Google: YouTube dl.google.com Others: dotCloud (Docker) SoundCloud
Canonical CloudFlare Mozilla ... golang.org/wiki/GoUsers (http://golang.org/wiki/GoUsers) -
Consistency Orthogonal features By Kenny Louie from Vancouver, Canada CC-BY-2.0
(http://creativecommons.org/licenses/by/2.0) , via Wikimedia Commons -
Readability “The ratio of time spent reading (code) versus writing
is well over 10 to 1 ... (therefore) making it easy to read makes it easier to write.” ― Robert C. Martin -
Built-in concurrency features “In a concurrent world, imperative is the
wrong default!” - Tim Sweeney Communicating Sequential Processes - Hoare (1978) -
Go and Java common aspects Go and Java are object
oriented garbage collected statically typed part of the C family -
Object oriented flavors Go is Object Oriented, but doesn't have
the keywords: c l a s s , e x t e n d s , or i m p l e m e n t s . -
Go types primitive types i n t , u i
n t , i n t 8 , u i n t 8 , . . . b o o l , s t r i n g f l o a t 3 2 , f l o a t 6 4 c o m p l e x 6 4 , c o m p l e x 1 2 8 structs s t r u c t { N a m e s t r i n g A g e i n t } slices and arrays [ ] i n t , [ 3 ] s t r i n g , [ ] s t r u c t { N a m e s t r i n g } maps m a p [ s t r i n g ] i n t -
Kinds of types (continued) pointers * i n t ,
* P e r s o n functions f u n c ( i n t , i n t ) i n t channels c h a n b o o l interfaces i n t e r f a c e { S t a r t ( ) S t o p ( ) } -
Type declarations t y p e [ n a m
e ] [ s p e c i f i c a t i o n ] P e r s o n is a s t r u c t type. t y p e P e r s o n s t r u c t { n a m e s t r i n g a g e i n t } C e l s i u s is a f l o a t 6 4 type. t y p e C e l s i u s f l o a t 6 4 -
Function declarations f u n c [ n a m
e ] ( [ p a r a m s ] ) [ r e t u r n v a l u e ] f u n c [ n a m e ] ( [ p a r a m s ] ) ( [ r e t u r n v a l u e s ] ) A sum function: f u n c s u m ( a i n t , b i n t ) i n t { r e t u r n a + b } A function with multiple returned values: f u n c d i v ( a , b i n t ) ( i n t , i n t ) r e t u r n a / b , a % b } Made clearer by naming the return values: f u n c d i v ( d e n , d i v i n t ) ( q , r e m i n t ) r e t u r n a / b , a % b } -
Method declarations f u n c ( [ r e
c e i v e r ] ) [ n a m e ] ( [ p a r a m s ] ) ( [ r e t u r n v a l u e s ] ) A method on a struct: f u n c ( p P e r s o n ) M a j o r ( ) b o o l { r e t u r n p . a g e > = 1 8 } But also a method on a f l o a t 6 4 : f u n c ( c C e l s i u s ) F r e e z i n g ( ) b o o l { r e t u r n c < = 0 } Constraint: Methods can be defined only on types declared in the same package. / / T h i s w o n ' t c o m p i l e f u n c ( s s t r i n g ) L e n g t h ( ) i n t { r e t u r n l e n ( s ) } -
Wait, pointers? Use & to obtain the address of a
variable. a : = " h e l l o " p : = & a Use * to dereference the pointer. f m t . P r i n t ( * p + " , w o r l d " ) No pointer arithmetic, no pointers to unsafe memory. a : = " h e l l o " p : = & a p + = 4 / / n o , y o u c a n ' t -
Why pointers? Control what you pass to functions. passing values,
no side-effects: f u n c d o u b l e ( x i n t ) { x * = 2 } passing pointers: side-effects possible: f u n c d o u b l e ( x * i n t ) { * x * = 2 } Control your memory layout. compare []Person and []*Person -
Method declarations on pointers Receivers behave like any other argument.
Pointers allow modifying the pointed receiver: f u n c ( p * P e r s o n ) I n c A g e ( ) { p . a g e + + } The method receiver is a copy of a pointer (pointing to the same address). Method calls on nil receivers are perfectly valid (and useful!). f u n c ( p * P e r s o n ) N a m e ( ) s t r i n g { i f p = = n i l { r e t u r n " a n o n y m o u s " } r e t u r n p . n a m e } -
Interfaces An interface is a set of methods. In Java:
i n t e r f a c e S w i t c h { v o i d o p e n ( ) ; v o i d c l o s e ( ) ; } In Go: t y p e O p e n C l o s e r i n t e r f a c e { O p e n ( ) C l o s e ( ) } -
It's all about satisfaction Java interfaces are satisfied explicitly. Go
interfaces are satisfied implicitly. Picture by Gorupdebesanez CC-BY-SA-3.0 (http://creativecommons.org/licenses/by-sa/3.0) , via Wikimedia Commons (http://commons.wikimedia.org/wiki/File%3ARolling_Stones_09.jpg) -
Go: implicit satisfaction If a type defines all the methods
of an interface, the type satisfies that interface. Benefits: fewer dependencies no type hierarchy organic composition -
FuncDraw: package parser Package p a r s e provides
a parser of strings into functions. f u n c P a r s e ( t e x t s t r i n g ) ( * F u n c , e r r o r ) { . . . } F u n c is a struct type, with an E v a l method. t y p e F u n c s t r u c t { . . . } f u n c ( p * F u n c ) E v a l ( x f l o a t 6 4 ) f l o a t 6 4 { . . . } -
FuncDraw: package draw Package draw generates images given a function.
f u n c D r a w ( f * p a r s e r . F u n c ) i m a g e . I m a g e { f o r x : = s t a r t ; x < e n d ; x + = i n c { y : = f . E v a l ( x ) . . . } } d r a w depends on p a r s e r makes testing hard Let's use an interface instead t y p e E v a l u a b l e i n t e r f a c e { E v a l ( f l o a t 6 4 ) f l o a t 6 4 } f u n c D r a w ( f E v a l u a b l e ) i m a g e . I m a g e { . . . } -
Inheritance vs composition Lots of articles have been written about
the topic. In general, composition is preferred to inheritance. Lets see why. -
Runner c l a s s R u n n
e r { p r i v a t e S t r i n g n a m e ; p u b l i c R u n n e r ( S t r i n g n a m e ) { t h i s . n a m e = n a m e ; } p u b l i c S t r i n g g e t N a m e ( ) { r e t u r n t h i s . n a m e ; } p u b l i c v o i d r u n ( T a s k t a s k ) { t a s k . r u n ( ) ; } p u b l i c v o i d r u n A l l ( T a s k [ ] t a s k s ) { f o r ( T a s k t a s k : t a s k s ) { r u n ( t a s k ) ; } } } -
RunCounter is-a Runner that counts c l a s s
R u n C o u n t e r e x t e n d s R u n n e r { p r i v a t e i n t c o u n t ; p u b l i c R u n C o u n t e r ( S t r i n g m e s s a g e ) { s u p e r ( m e s s a g e ) ; t h i s . c o u n t = 0 ; } @ O v e r r i d e p u b l i c v o i d r u n ( T a s k t a s k ) { c o u n t + + ; s u p e r . r u n ( t a s k ) ; } @ O v e r r i d e p u b l i c v o i d r u n A l l ( T a s k [ ] t a s k s ) { c o u n t + = t a s k s . l e n g t h ; s u p e r . r u n A l l ( t a s k s ) ; } p u b l i c i n t g e t C o u n t ( ) { r e t u r n c o u n t ; } } -
Let's run and count What will this code print? R
u n C o u n t e r r u n n e r = n e w R u n C o u n t e r ( " m y r u n n e r " ) ; T a s k [ ] t a s k s = { n e w T a s k ( " o n e " ) , n e w T a s k ( " t w o " ) , n e w T a s k ( " t h r e e " ) } ; r u n n e r . r u n A l l ( t a s k s ) ; S y s t e m . o u t . p r i n t f ( " % s r a n % d t a s k s \ n " , r u n n e r . g e t N a m e ( ) , r u n n e r . g e t C o u n t ( ) ) ; Of course, this prints: r u n n i n g o n e r u n n i n g t w o r u n n i n g t h r e e m y r u n n e r r a n 6 t a s k s Wait! How many? -
My runner ran 6 tasks? Six? Inheritance causes: weak encapsulation,
tight coupling, surprising bugs. -
Solution: use composition c l a s s R u
n C o u n t e r { p r i v a t e R u n n e r r u n n e r ; p r i v a t e i n t c o u n t ; p u b l i c R u n C o u n t e r ( S t r i n g m e s s a g e ) { t h i s . r u n n e r = n e w R u n n e r ( m e s s a g e ) ; t h i s . c o u n t = 0 ; } p u b l i c v o i d r u n ( T a s k t a s k ) { c o u n t + + ; r u n n e r . r u n ( t a s k ) ; } p u b l i c v o i d r u n A l l ( T a s k [ ] t a s k s ) { c o u n t + = t a s k s . l e n g t h ; r u n n e r . r u n A l l ( t a s k s ) ; } / / c o n t i n u e d o n n e x t s l i d e . . . -
Solution: use composition (continued) p u b l i c
i n t g e t C o u n t ( ) { r e t u r n c o u n t ; } p u b l i c S t r i n g g e t N a m e ( ) { r e t u r n r u n n e r . g e t N a m e ( ) ; } } -
Solution: use composition (continued) Pros The bug is gone! R
u n n e r is completely independent of R u n C o u n t e r . The creation of the R u n n e r can be delayed until (and if) needed. Cons We need to explicitly define the R u n n e r methods on R u n C o u n t e r : p u b l i c S t r i n g g e t N a m e ( ) { r e t u r n r u n n e r . g e t N a m e ( ) ; } This can cause lots of repetition, and eventually bugs. -
There's no inheritance in Go Let's use composition directly: t
y p e R u n n e r s t r u c t { n a m e s t r i n g } f u n c ( r * R u n n e r ) N a m e ( ) s t r i n g { r e t u r n r . n a m e } f u n c ( r * R u n n e r ) R u n ( t T a s k ) { t . R u n ( ) } f u n c ( r * R u n n e r ) R u n A l l ( t s [ ] T a s k ) { f o r _ , t : = r a n g e t s { r . R u n ( t ) } } All very similar to the Java version. -
RunCounter R u n C o u n t e
r has a R u n n e r field. t y p e R u n C o u n t e r s t r u c t { r u n n e r R u n n e r c o u n t i n t } f u n c N e w R u n C o u n t e r ( n a m e s t r i n g ) * R u n C o u n t e r { r e t u r n & R u n C o u n t e r { r u n n e r : R u n n e r { n a m e } } } f u n c ( r * R u n C o u n t e r ) R u n ( t T a s k ) { r . c o u n t + + r . r u n n e r . R u n ( t ) } f u n c ( r * R u n C o u n t e r ) R u n A l l ( t s [ ] T a s k ) { r . c o u n t + = l e n ( t s ) r . r u n n e r . R u n A l l ( t s ) } f u n c ( r * R u n C o u n t e r ) C o u n t ( ) i n t { r e t u r n r . c o u n t } f u n c ( r * R u n C o u n t e r ) N a m e ( ) s t r i n g { r e t u r n r . r u n n e r . N a m e ( ) } -
Composition in Go Same pros and cons as the composition
version in Java. We also have the boilerplate to proxy methods from R u n n e r . f u n c ( r * R u n C o u n t e r ) N a m e ( ) s t r i n g { r e t u r n r . r u n n e r . N a m e ( ) } But we can remove it! -
Struct embedding Expressed in Go as unnamed fields in a
struct. It is still composition. The fields and methods of the embedded type are defined on the embedding type. Similar to inheritance, but the embedded type doesn't know it's embedded. -
Example of struct embedding Given a type P e r
s o n : t y p e P e r s o n s t r u c t { N a m e s t r i n g } f u n c ( p P e r s o n ) I n t r o d u c e ( ) { f m t . P r i n t l n ( " H i , I ' m " , p . N a m e ) } We can define a type E m p l o y e e embedding P e r s o n : t y p e E m p l o y e e s t r u c t { P e r s o n E m p l o y e e I D i n t } All fields and methods from P e r s o n are available on E m p l o y e e : v a r e E m p l o y e e e . N a m e = " P e t e r " e . E m p l o y e e I D = 1 2 3 4 e . I n t r o d u c e ( ) -
Struct embedding t y p e R u n C
o u n t e r 2 s t r u c t { R u n n e r c o u n t i n t } f u n c N e w R u n C o u n t e r 2 ( n a m e s t r i n g ) * R u n C o u n t e r 2 { r e t u r n & R u n C o u n t e r 2 { R u n n e r { n a m e } , 0 } } f u n c ( r * R u n C o u n t e r 2 ) R u n ( t T a s k ) { r . c o u n t + + r . R u n n e r . R u n ( t ) } f u n c ( r * R u n C o u n t e r 2 ) R u n A l l ( t s [ ] T a s k ) { r . c o u n t + = l e n ( t s ) r . R u n n e r . R u n A l l ( t s ) } f u n c ( r * R u n C o u n t e r 2 ) C o u n t ( ) i n t { r e t u r n r . c o u n t } -
Is struct embedding like inheritance? No, it is better! It
is composition. You can't reach into another type and change the way it works. Method dispatching is explicit. It is more general. Struct embedding of interfaces. -
Is struct embedding like inheritance? Struct embedding is selective. /
/ W r i t e C o u n t e r t r a c k s t h e t o t a l n u m b e r o f b y t e s w r i t t e n . t y p e W r i t e C o u n t e r s t r u c t { i o . R e a d W r i t e r c o u n t i n t } f u n c ( w * W r i t e C o u n t e r ) W r i t e ( b [ ] b y t e ) ( i n t , e r r o r ) { w . c o u n t + = l e n ( b ) r e t u r n w . R e a d W r i t e r . W r i t e ( b ) } WriteCounter can be used with any i o . R e a d W r i t e r . f u n c m a i n ( ) { b u f : = & b y t e s . B u f f e r { } w : = & W r i t e C o u n t e r { R e a d W r i t e r : b u f } f m t . F p r i n t f ( w , " H e l l o , g o p h e r s ! \ n " ) f m t . P r i n t f ( " P r i n t e d % v b y t e s " , w . c o u n t ) } Run -
Easy mocking What if we wanted to fake a part
of a n e t . C o n n ? t y p e C o n n i n t e r f a c e { R e a d ( b [ ] b y t e ) ( n i n t , e r r e r r o r ) W r i t e ( b [ ] b y t e ) ( n i n t , e r r e r r o r ) C l o s e ( ) e r r o r L o c a l A d d r ( ) A d d r R e m o t e A d d r ( ) A d d r S e t D e a d l i n e ( t t i m e . T i m e ) e r r o r S e t R e a d D e a d l i n e ( t t i m e . T i m e ) e r r o r S e t W r i t e D e a d l i n e ( t t i m e . T i m e ) e r r o r } I want to test h a n d l e C o n : f u n c h a n d l e C o n n ( c o n n n e t . C o n n ) { We could create a f a k e C o n n and define all the methods of C o n n on it. But that's a lot of boring code. -
Struct embedding of interfaces WARNING : Cool stuff If a
type T has an embedded field of a type E, all the methods of E will be defined on T. Therefore, if E is an interface T satisfies E. -
Struct embedding of interfaces (continued) We can test h a
n d l e C o n with the l o o p B a c k type. t y p e l o o p B a c k s t r u c t { n e t . C o n n b u f b y t e s . B u f f e r } Any calls to the methods of n e t . C o n n will fail, since the field is nil. We redefine the operations we support: f u n c ( c * l o o p B a c k ) R e a d ( b [ ] b y t e ) ( i n t , e r r o r ) { r e t u r n c . b u f . R e a d ( b ) } f u n c ( c * l o o p B a c k ) W r i t e ( b [ ] b y t e ) ( i n t , e r r o r ) { r e t u r n c . b u f . W r i t e ( b ) } -
Concurrency It is part of the language, not a library.
Based on two concepts: goroutines: lightweight threads channels: typed pipes used to communicate and synchronize between goroutines So cheap you can use them whenever you want. -
Sleep and talk f u n c s l e
e p A n d T a l k ( t t i m e . D u r a t i o n , m s g s t r i n g ) { t i m e . S l e e p ( t ) f m t . P r i n t f ( " % v " , m s g ) } We want a message per second. What if we started all the s l e e p A n d T a l k concurrently? Just add g o ! f u n c m a i n ( ) { s l e e p A n d T a l k ( 0 * t i m e . S e c o n d , " H e l l o " ) s l e e p A n d T a l k ( 1 * t i m e . S e c o n d , " G o p h e r s ! " ) s l e e p A n d T a l k ( 2 * t i m e . S e c o n d , " W h a t ' s " ) s l e e p A n d T a l k ( 3 * t i m e . S e c o n d , " u p ? " ) } Run -
Concurrent sleep and talk That was fast ... When the
m a i n goroutine ends, the program ends. f u n c m a i n ( ) { g o s l e e p A n d T a l k ( 0 * t i m e . S e c o n d , " H e l l o " ) g o s l e e p A n d T a l k ( 1 * t i m e . S e c o n d , " G o p h e r s ! " ) g o s l e e p A n d T a l k ( 2 * t i m e . S e c o n d , " W h a t ' s " ) g o s l e e p A n d T a l k ( 3 * t i m e . S e c o n d , " u p ? " ) } Run -
Concurrent sleep and talk with more sleeping But synchronizing with
S l e e p is a bad idea. f u n c m a i n ( ) { g o s l e e p A n d T a l k ( 0 * t i m e . S e c o n d , " H e l l o " ) g o s l e e p A n d T a l k ( 1 * t i m e . S e c o n d , " G o p h e r s ! " ) g o s l e e p A n d T a l k ( 2 * t i m e . S e c o n d , " W h a t ' s " ) g o s l e e p A n d T a l k ( 3 * t i m e . S e c o n d , " u p ? " ) t i m e . S l e e p ( 4 * t i m e . S e c o n d ) } Run -
Communicating through channels s l e e p A n
d T a l k sends the string into the channel instead of printing it. f u n c s l e e p A n d T a l k ( s e c s t i m e . D u r a t i o n , m s g s t r i n g , c c h a n s t r i n g ) { t i m e . S l e e p ( s e c s * t i m e . S e c o n d ) c < - m s g } We create the channel and pass it to s l e e p A n d T a l k , then wait for the values to be sent. f u n c m a i n ( ) { c : = m a k e ( c h a n s t r i n g ) g o s l e e p A n d T a l k ( 0 , " H e l l o " , c ) g o s l e e p A n d T a l k ( 1 , " G o p h e r s ! " , c ) g o s l e e p A n d T a l k ( 2 , " W h a t ' s " , c ) g o s l e e p A n d T a l k ( 3 , " u p ? " , c ) f o r i : = 0 ; i < 4 ; i + + { f m t . P r i n t f ( " % v " , < - c ) } } Run -
Let's count on the web We receive the next id
from a channel. v a r n e x t I D = m a k e ( c h a n i n t ) f u n c h a n d l e r ( w h t t p . R e s p o n s e W r i t e r , q * h t t p . R e q u e s t ) { f m t . F p r i n t f ( w , " < h 1 > Y o u g o t % v < h 1 > " , < - n e x t I D ) } We need a goroutine sending ids into the channel. localhost:8080/next (http://localhost:8080/next) f u n c m a i n ( ) { h t t p . H a n d l e F u n c ( " / n e x t " , h a n d l e r ) g o f u n c ( ) { f o r i : = 0 ; ; i + + { n e x t I D < - i } } ( ) h t t p . L i s t e n A n d S e r v e ( " l o c a l h o s t : 8 0 8 0 " , n i l ) } Run -
Let's fight! s e l e c t allows us
to chose among multiple channel operations. Go - localhost:8080/fight?usr=go (http://localhost:8080/fight?usr=go) Java - localhost:8080/fight?usr=java (http://localhost:8080/fight?usr=java) v a r b a t t l e = m a k e ( c h a n s t r i n g ) f u n c h a n d l e r ( w h t t p . R e s p o n s e W r i t e r , q * h t t p . R e q u e s t ) { s e l e c t { c a s e b a t t l e < - q . F o r m V a l u e ( " u s r " ) : f m t . F p r i n t f ( w , " Y o u w o n ! " ) c a s e w o n : = < - b a t t l e : f m t . F p r i n t f ( w , " Y o u l o s t , % v i s b e t t e r t h a n y o u " , w o n ) } } Run -
Chain of gophers f u n c f ( l
e f t , r i g h t c h a n i n t ) { l e f t < - 1 + < - r i g h t } f u n c m a i n ( ) { s t a r t : = t i m e . N o w ( ) c o n s t n = 1 0 0 0 l e f t m o s t : = m a k e ( c h a n i n t ) r i g h t : = l e f t m o s t l e f t : = l e f t m o s t f o r i : = 0 ; i < n ; i + + { r i g h t = m a k e ( c h a n i n t ) g o f ( l e f t , r i g h t ) l e f t = r i g h t } g o f u n c ( c c h a n i n t ) { c < - 0 } ( r i g h t ) f m t . P r i n t l n ( < - l e f t m o s t , t i m e . S i n c e ( s t a r t ) ) } Run -
Concurrency is very powerful And there's lots to learn! Go
Concurrency Patterns (http://talks.golang.org/2012/concurrency.slide#1) , by Rob Pike Advanced Concurrency Patterns (http://talks.golang.org/2013/advconc.slide#1) , by Sameer Ajmani Concurrency is not Parellelism (http://talks.golang.org/2012/waza.slide#1) , by Rob Pike -
In conclusion Go is simple, consistent, readable, and fun. All
types are equal methods on any type Implicit interfaces Structural typing Less dependencies Code testable and reusable Use composition instead of inheritance Struct embedding to remove boilerplate. Struct embedding of interfaces to satisfy them fast. Concurrency is awesome, and you should check it out. -
What to do next? Learn Go on your browser with
tour.golang.org (http://tour.golang.org) Find more about Go on golang.org (http://golang.org) Join the community at golang-nuts (https://groups.google.com/forum/#!forum/Golang-nuts) Link to the slides talks.golang.org/2014/go4java.slide (http://talks.golang.org/2014/go4java.slide) -
Thank you Francesc Campoy Gopher and Developer Advocate Google @francesc
(http://twitter.com/francesc) [email protected] (mailto:[email protected])