A journey in software development.
Or, how to do what you want and get away with it.
17 March 2017
Dmitri Shuralyov
Dmitri Shuralyov
A video of this talk was recorded at University of Ontario Institute of Technology (UOIT) on March 17, 2017.
Video: www.youtube.com/watch?v=XSd9OAHVXMI
2Which ones have you used? Heard of?
Which ones do you like?
What about frontend?
3Preface:
You may have heard of Go.
It's my favorite language. I think you'll like it, too.
6An open source (BSD licensed) project:
gc
and gccgo
),Go is Object Oriented, but not in the usual way.
The result: simple pieces connected by small interfaces.
8Go provides CSP-like concurrency primitives.
The result: comprehensible concurrent code.
9Go is about composition, concurrency, and gophers.
Keep that in mind.
11package main import "fmt" func main() { fmt.Println("Hello, Go.") }
package main import ( "fmt" "log" "net/http" ) const listenAddr = "localhost:4000" func main() { http.HandleFunc("/", handler) err := http.ListenAndServe(listenAddr, nil) if err != nil { log.Fatal(err) } } func handler(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "Hello, web.") }
package main import ( "fmt" "log" "net" ) const listenAddr = "localhost:4000" func main() { l, err := net.Listen("tcp", listenAddr) if err != nil { log.Fatal(err) } for { c, err := l.Accept() if err != nil { log.Fatal(err) } fmt.Fprintln(c, "Hello!") c.Close() } }
Hey neato! We just used Fprintln
to write to a net connection.
That's because a Fprintln
writes to an io.Writer
, and net.Conn
is an io.Writer
.
fmt.Fprintln(c, "Hello!")
func Fprintln(w io.Writer, a ...interface{}) (n int, err error)
type Writer interface { Write(p []byte) (n int, err error) }
type Conn interface { Read(b []byte) (n int, err error) Write(b []byte) (n int, err error) Close() error // ... some additional methods omitted ... }
package main import ( "io" "log" "net" ) const listenAddr = "localhost:4000" func main() { l, err := net.Listen("tcp", listenAddr) if err != nil { log.Fatal(err) } for { c, err := l.Accept() if err != nil { log.Fatal(err) } io.Copy(c, c) } }
Goroutines are lightweight threads that are managed by the Go runtime. To run a function in a new goroutine, just put "go"
before the function call.
package main import ( "fmt" "time" ) func main() { go say("let's go!", 3) go say("ho!", 2) go say("hey!", 1) time.Sleep(4 * time.Second) } func say(text string, secs int) { time.Sleep(time.Duration(secs) * time.Second) fmt.Println(text) }
package main import ( "io" "log" "net" ) const listenAddr = "localhost:4000" func main() { l, err := net.Listen("tcp", listenAddr) if err != nil { log.Fatal(err) } for { c, err := l.Accept() if err != nil { log.Fatal(err) } go io.Copy(c, c) } }
Let's look at a simple program, based on the "chat roulette" site.
In short:
The chat program is similar to the echo program. With echo, we copy a connection's incoming data back to the same connection.
For chat, we must copy the incoming data from one user's connection to another's.
Copying the data is easy. The hard part is matching one partner with another.
20Goroutines communicate via channels. A channel is a typed conduit that may be synchronous (unbuffered) or asynchronous (buffered).
package main import "fmt" func main() { ch := make(chan int) go fibs(ch) for i := 0; i < 20; i++ { fmt.Println(<-ch) } } func fibs(ch chan int) { i, j := 0, 1 for { ch <- j i, j = j, i+j } }
A select statement is like a switch, but it selects over channel operations (and chooses exactly one of them).
package main import ( "fmt" "time" ) func main() { ticker := time.NewTicker(time.Millisecond * 250) boom := time.After(time.Second * 1) for { select { case <-ticker.C: fmt.Println("tick") case <-boom: fmt.Println("boom!") return } } }
In the accept loop, we replace the call to io.Copy
:
for { c, err := l.Accept() if err != nil { log.Fatal(err) } go io.Copy(c, c) }
with a call to a new function, match
:
for { c, err := l.Accept() if err != nil { log.Fatal(err) } go match(c) }
The match
function simultaneously tries to send and receive a connection on a channel.
var partner = make(chan io.ReadWriteCloser) func match(c io.ReadWriteCloser) { fmt.Fprint(c, "Waiting for a partner...") select { case partner <- c: // now handled by the other goroutine case p := <-partner: chat(p, c) } }
The chat function sends a greeting to each connection and then copies data from one to the other, and vice versa.
Notice that it launches another goroutine so that the copy operations may happen concurrently.
func chat(a, b io.ReadWriteCloser) { fmt.Fprintln(a, "Found one! Say hi.") fmt.Fprintln(b, "Found one! Say hi.") go io.Copy(a, b) io.Copy(b, a) }
It's important to clean up when the conversation is over. To do this we send the error value from each io.Copy
call to a channel, log any non-nil errors, and close both connections.
func chat(a, b io.ReadWriteCloser) { fmt.Fprintln(a, "Found one! Say hi.") fmt.Fprintln(b, "Found one! Say hi.") errc := make(chan error, 1) go cp(a, b, errc) go cp(b, a, errc) if err := <-errc; err != nil { log.Println(err) } a.Close() b.Close() }
func cp(w io.Writer, r io.Reader, errc chan<- error) { _, err := io.Copy(w, r) errc <- err }
Want to see WebSockets, markov chains, learning bots, TCP and HTTP at once?
Video: vimeo.com/53221560
Slides: talks.golang.org/2012/chat.slide
30Started programming at 14.
Excited by the ability to create video games.
32Moved towards C++ because it's fast, cross-platform, compiles native binaries.
39Master's degree.
I realized I wanted to create tools that help people (more directly than games).
Also grew frustrated with C++, extremely motivated to do something about it.
41Bret Victor gave an incredible talk on following a guiding principle.
Video: vimeo.com/36579366
42Started to work on an experimental project.
Submitted it to a Live Programming Contest at LIVE 2013 Workshop. Won 1st place.
Got to visit San Francisco.
It helped me land my first job at a startup in San Francisco.
Startups with lots of raised money are bottlenecked on people.
A real-life story about a star.
52A story about a talented engineer's ups and downs.
57I like open source because people aren't locked out from being able to contribute.
It's normal to start small to test the waters.
Go is probably my first programming language experience with a "community".
There are conferences, meetups, talks, slack group, etc.
63Working full time on open source. Including personal projects (Go Package Store, etc.).
Contributing to Go, GopherJS.
First half of 2017, I'm prioritizing things I think are important (WebAssembly, etc.).
Second half, I'll be prioritizing ideas that can lead to profitability/sustainability.
I want to use Go, but it's hard to ignore the browser in 2017.
68Go already runs on many platforms.
# Desktop OSes. GOOS=darwin GOARCH=arm64 go build GOOS=linux GOARCH=amd64 go build GOOS=windows GOARCH=arm64 go build GOOS=plan9 GOARCH=amd64 go build # Plan 9. GOOS=linux GOARCH=s390x go build # Linux on IBM z Systems. # Mobile OSes. GOOS=darwin GOARCH=arm64 go build # iOS. GOOS=android GOARCH=arm go build # Android.
Go already runs on many platforms.
70How about one more?
71GopherJS is a compiler that compiles Go to JavaScript, which runs in browsers.
//gopherjs:blocking
.//gopherjs:blocking
.Video: www.thedotpost.com/2016/10/dmitri-shuralyov-go-in-the-browser
Slides: dmitri.shuralyov.com/talks/2016/Go-in-the-browser/Go-in-the-browser.slide
73