go for rubyists
DESCRIPTION
This presentation tries to introduce people that are familiar to ruby with Go.TRANSCRIPT
Go for RubystsThiago Pradi
RubyConf Argentina - 2013
whoami
• Thiago Pradi • Brazilian! • Software Developer @ JobScore • Bachelor of Computer Science @ FURB • Octopus author / maintainer
Thiago was a happy Ruby developer...
that wanted to learn something new!
Why not a new language? ...
... Maybe Go!
Google trends - Golang
What is Go?
Go• Initially developed at Google • Created by Ken Thompson (Unix), Rob Pike
(Plan 9), and Russ Cox (libtask) • Development started in 2007 • First release in 2009 (Fairly new!)
Go is an open source programming environment that makes it easy to build
simple, reliable, and efficient software.
Source: golang.org
Features
• New • Concurrent • Compiled • Garbaged-collected • Simple & Fun!
Language focus• System programming • Networked / multi-core • Fast • Compatible with C • Best of static typed language and dynamic
languages
Basic Concepts
package main import "fmt" func main() { fmt.Println("Hello World") }
C-Like Syntaxfunc main() { fmt.Printf("Animal with name %s and age %d", "Anaconda", 32) }
Compiled language
Typesystem
Strong typesfunc main() { var str string var value int str = "abc" value = 123 str + value}
Static Typed// types.gofunc main() { var a string a = 123 }
with dynamic castingpackage main import ( "fmt" ) func main() { a := 123 fmt.Printf("Value of a: %d", a) }
User defined typespackage main; type Animal struct { Name string Age int } func main() { var anaconda Animal }
Compiler
• Uses GCC as back end • Checks for unused packages and variables • Checks types and return values
Go tools
• go fmt -> format your source code (cool!) • go get -> manage and install your
dependencies • go build / run -> compile and run your
program • go test -> run your tests
Organizing codepackage string_processing; func Process(str string) { // Code code code }
package mainimport "string_processing" func main() { string_processing.Process("foobar") }
Workspace
• The default way to organize code in Go • Build to work with OpenSource repositories • Directories src, bin and pkg
Encoding
• Source code: UTF-8 • Strings: UTF-8
Comparing it to Ruby..
Disclaimer
Object Orientation
• Ruby uses classes / methods • Golang uses Interfaces, adding methods to
data structures.
Objects in Ruby
• Ruby Object model • Module / Class with methods • Support for inheritance and composition • Everything is an object
class Animal attr_accessor :name def initialize(name) self.name = name end def say_something puts "HEY HEY" endend!
a = Animal.new("Duck")a.say_something
Objects in Go
• No inheritance • Data types to define content • Methods define the operations • Interfaces define the behavior of the
“object” (data type)
package mainimport "fmt"type Animal struct { Name string }!
func (a *Animal) SaySomething() { fmt.Println("HEY HEY")}!
func main() { a := new(Animal) a.Name = "Duck" a.SaySomething()}
Error Handling
• Ruby uses Exceptions, with begin / rescue / ensure
• Golang use return values with error code (!!!)
Error handling - Ruby
• Errors inherit from Exception class • Flow control with begin / rescue / ensure • Don’t need to treat every exception
file = File.open("/tmp/mel.txt", "w")!
begin # does some file processingrescue puts "Failed to process"ensure file.closeend
Error Handling - Go
• No exceptions • Errors should implement “Error” interface • Erros should be returned as additional value • Every error should be treated (unless you want
to do a “Gambiarra”)
Off-topic: Gambiarra
func main() { fmt.Println("Starting MEL server")!
listener, err := net.Listen("tcp", "0.0.0.0:2653")!
if err != nil { fmt.Println("Error starting the server") os.Exit(1) }}
defer / panic / recover
• Defer is compared to “ensure” in ruby • Panic / recover are similar to exception, but
only should be used for internal APIs, never for external APIs.
• Panic is used for runtime errors, like array out of bound
func EchoFunction(connection net.Conn, ss *command_parser.ServerStorage) { defer connection.Close()!
buf := make([]byte, 1024) n, err := connection.Read(buf) // Heavy logic}
func main() { PanicFunction() fmt.Println("This will not be printed")}!
func PanicFunction() { defer func() { fmt.Println("Called defer function") }() panic("PANIC PANIC PANIC") fmt.Println("This will not be printed")}
func main() { PanicFunction() fmt.Println("This will be printed")}!
func PanicFunction() { defer func() { if e := recover(); e != nil { fmt.Printf("Recovered from %s \n", e) } }() panic("PANIC PANIC PANIC") fmt.Println("This will not be printed")}
Concurrency
• Ruby -> Threads / Fibers / EventPool / Actors • Go -> Goroutines
Concurrency - Ruby
• Threads (with shared memory) • Fibers • EventMachine (Event loop / Reactor pattern) • Celluloid (Actor based)
require 'net/http'!
content = []!
thread = Thread.new do uri = URI("http://triremi.com/") content << Net::HTTP.get(uri)end!
thread.join
Concurrency - Go• Goroutines! • Lightweight thread implementation • Communications between goroutines using
channels • Managed by the Go Scheduler • Mapped to a few different OS processes
for { connection, err := listener.Accept()!
if err != nil { fmt.Println("Error accepting the socket") os.Exit(2) }!
EchoFunction(connection, ss) }
for { connection, err := listener.Accept()!
if err != nil { fmt.Println("Error accepting the socket") os.Exit(2) }!
go EchoFunction(connection, ss) }
import "fmt"!
func main() { messages := make(chan string)!
go PingFunction(messages)!
msg := <-messages!
fmt.Println(msg)}!
func PingFunction(messages chan string) { messages <- "ping"}
// Make the channel with the number of connectionschannels := make(chan ChannelResult, number_of_connections)!
// Start the request in a new goroutinego makeRequest(address, start_byte, end_byte, out, channels)!
//Wait for the result of the goroutine in the channelfor(loop_var < number_of_connections) { chan_res := <-channels // Process the result and save to the file}
Testing
• Ruby has a built in framework (Test::Unit) and a lot of alternatives
• Go also has a default framework for testing, and some early-stages alternatives
Testing in Ruby
• Lots of frameworks: Test::Unit, rspec, MiniTest, Bacon…
• Frameworks with lots of assertions and pre-defined macros
• Easy to describe behavior (BDD) • Everyone tests.. (RIGHT?)
require "spec_helper"!
describe Octopus::Model do describe "#using method" do it "should return self" do User.using(:canada).should be_a(Octopus::ScopeProxy) end endend
Testing in Go• Light framework with the language, with a few
new options • Don’t come with assertions / macros • Tests have the same package as the
application code • Tests stay in the same directory of the
application code
package command_parser;!
import ( “testing" )!
func Test_parseSetCommand (t *testing.T) { ss := new(ServerStorage)!
str := ss.ParseCommand("SET thiagopradi valor")!
if ss.Dict["thiagopradi"] != "valor" { t.Error("failed to set thiagopradi key") } }}
But, what about some real use cases?
Companies
• Google (dl.google.com, youtube) • SoundCloud • Heroku • CloudFlare • Ubuntu
Open Source
• https://github.com/youtube/vitess - Process and tools for scaling MySQL
• https://github.com/dotcloud/docker - Open Source application container engine
• https://github.com/burke/zeus - Rails preloader
Mine
• YADM - Yet another download manager (https://github.com/tchandy/yadm)
• Brainfuck compiler (https://github.com/tchandy/bf_compiler_go)
• Mel - key-value database (https://github.com/tchandy/mel)
Wrapping up…• Cool language • Concurrent and networked • Benefits from both dynamic and static
languages • Modern • Give it a try!
One more thing..
GOLang!
GOLang!
Thank you!
• www.thiagopradi.net • twitter.com/thiagopradi • [email protected]