Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Ruby Alternatives

Ruby Alternatives

The story for the future of parallelism in Ruby is looking pretty bleak. Matz just said at Euruko that it will be a while (years) before the notorious Global Interpreter Lock is gone.

So I've been on a journey to find a more modern replacement that gives me the same joy that I feel when I write Ruby. It turns out that with modern programming languages, it's actually not that hard to learn a new technology.

I've found some great candidates in Go, Elixir and a newcomer Crystal. I'll dig in and show you some code and you can decide if maybe, you too, want to enter Parallel World with me!

Aaron Cruz

March 19, 2016
Tweet

More Decks by Aaron Cruz

Other Decks in Technology

Transcript

  1. E v e r y t w o y e

    a r s 2 t i m e s f a s t e r
  2. – M AT Z , T H I S M

    O R N I N G “Thread is too hard”
  3. We ’ re l i v i n g i

    n a p o s t M o o re w o r l d
  4. – M AT Z , T H I S M

    O R N I N G “[Ruby 3 will be finished] hopefully within this decade”
  5. – S E R D A R D O G

    R U Y O L “Nobody should only learn one programming language”
  6. Go

  7. Go

  8. Go

  9. W h i r l w i n d To

    u r J u s t t o s h ow t h e s e l a n g u a g e s a re n ’ t s c a r y
  10. Pro b l e m s A t G o

    o g l e • developer productivity
  11. • developer productivity • header files are inefficient • classic

    type systems (C++, Java) are cumbersome and pushing people toward dynamic languages Pro b l e m s A t G o o g l e
  12. • developer productivity • header files are inefficient • classic

    type systems (C++, Java) are cumbersome and pushing people toward dynamic languages • garbage collection not supported by most systems languages Pro b l e m s A t G o o g l e
  13. • developer productivity • header files are inefficient • classic

    type systems (C++, Java) are cumbersome and pushing people toward dynamic languages • garbage collection not supported by most systems languages • multicore computers on the rise Pro b l e m s A t G o o g l e
  14. Fe at u re s O f G o •

    incredibly fast compilation
  15. Fe at u re s O f G o •

    incredibly fast compilation • dependency model simplified
  16. Fe at u re s O f G o •

    incredibly fast compilation • dependency model simplified • light weight static type system
  17. Fe at u re s O f G o •

    incredibly fast compilation • dependency model simplified • light weight static type system • garbage collector
  18. Fe at u re s O f G o •

    incredibly fast compilation • dependency model simplified • light weight static type system • garbage collector • multicore first
  19. S t r u c t s a n d

    M e t h o d s
  20. func (r Rubyist) TellMeAboutYourself() { fmt.Printf("Hi. My name is %s

    and I love %s!\n", r.Name, strings.Join(r.FavoriteGems, ", ")) }
  21. func (r Rubyist) TellMeAboutYourself() { fmt.Printf("Hi. My name is %s

    and I love %s!\n", r.Name, strings.Join(r.FavoriteGems, ", ")) }
  22. func (r Rubyist) TellMeAboutYourself() { fmt.Printf("Hi. My name is %s

    and I love %s!\n", r.Name, strings.Join(r.FavoriteGems, ", ")) }
  23. func (r Rubyist) TellMeAboutYourself() { fmt.Printf("Hi. My name is %s

    and I love %s!\n", r.Name, strings.Join(r.FavoriteGems, ", ")) }
  24. func (r Rubyist) TellMeAboutYourself() { fmt.Printf("Hi. My name is %s

    and I love %s!\n", r.Name, strings.Join(r.FavoriteGems, ", ")) }
  25. func main() { gems := []string{"pry", "faraday", "sequel"} aaron :=

    Rubyist{Name: "Aaron", FavoriteGems: gems} aaron.TellMeAboutYourself() }
  26. G o ’s Co n c u r re n

    c y Patt e r n s
  27. G o ’s Co n c u r re n

    c y Patt e r n s • goroutines
  28. G o ’s Co n c u r re n

    c y Patt e r n s • goroutines • channels
  29. func main() { messages := make(chan string) go func() {

    time.Sleep(time.Second * 1) messages <- "ping" }() fmt.Println("Blocking on channel") msg := <-messages fmt.Println(msg) }
  30. func main() { messages := make(chan string) go func() {

    time.Sleep(time.Second * 1) messages <- "ping" }() fmt.Println("Blocking on channel") msg := <-messages fmt.Println(msg) }
  31. func main() { messages := make(chan string) go func() {

    time.Sleep(time.Second * 1) messages <- "ping" }() fmt.Println("Blocking on channel") msg := <-messages fmt.Println(msg) }
  32. func main() { messages := make(chan string) go func() {

    time.Sleep(time.Second * 1) messages <- "ping" }() fmt.Println("Blocking on channel") msg := <-messages fmt.Println(msg) }
  33. func main() { messages := make(chan string) go func() {

    time.Sleep(time.Second * 1) messages <- "ping" }() fmt.Println("Blocking on channel") msg := <-messages fmt.Println(msg) }
  34. func main() { messages := make(chan string) go func() {

    time.Sleep(time.Second * 1) messages <- "ping" }() fmt.Println("Blocking on channel") msg := <-messages fmt.Println(msg) }
  35. P r a c t i c a l U

    s e C a s e
  36. P r a c t i c a l U

    s e C a s e S i d e k i q w o r k e r s
  37. func transcode(message *workers.Msg) { // you have access to //

    create a map of the args from the worker call payload := message.Args().Map() // Transcode it with your transcode package transcoder.Transcode(payload["path"].(string)) }
  38. func sendMail(message *workers.Msg) { payload := message.Args().Map() mailerType := payload["mailer"].(string)

    // Factory function // Infers type and returns interface: Send(string) client := mailer.NewMailer(mailerType) client.Send(payload["email_address"].(string)) }
  39. func main() { workers.Configure(map[string]string{ // location of redis instance "server":

    "localhost:6379", // instance of the database "database": "0", // number of connections to keep open with redis "pool": "30", // unique process id for this instance of workers // (for proper recovery of inprogress jobs on crash) "process": "1", }) 
 //… continued in next slide }
  40. func main() { // pull messages from "sendMail" with concurrency

    of 10 workers.Process("emails", sendMail, 10) // pull messages from "transcode" with concurrency of 20 workers.Process("transcode", transcode, 20) // stats will be available at http://localhost:8080/stats go workers.StatsServer(8080) // Blocks until process is told to exit via unix signal workers.Run() }
  41. func main() { // pull messages from "sendMail" with concurrency

    of 10 workers.Process("emails", sendMail, 10) // pull messages from "transcode" with concurrency of 20 workers.Process("transcode", transcode, 20) // stats will be available at http://localhost:8080/stats go workers.StatsServer(8080) // Blocks until process is told to exit via unix signal workers.Run() }
  42. func main() { // pull messages from "sendMail" with concurrency

    of 10 workers.Process("emails", sendMail, 10) // pull messages from "transcode" with concurrency of 20 workers.Process("transcode", transcode, 20) // stats will be available at http://localhost:8080/stats go workers.StatsServer(8080) // Blocks until process is told to exit via unix signal workers.Run() }
  43. func main() { // pull messages from "sendMail" with concurrency

    of 10 workers.Process("emails", sendMail, 10) // pull messages from "transcode" with concurrency of 20 workers.Process("transcode", transcode, 20) // stats will be available at http://localhost:8080/stats go workers.StatsServer(8080) // Blocks until process is told to exit via unix signal workers.Run() }
  44. func main() { // pull messages from "sendMail" with concurrency

    of 10 workers.Process("emails", sendMail, 10) // pull messages from "transcode" with concurrency of 20 workers.Process("transcode", transcode, 20) // stats will be available at http://localhost:8080/stats go workers.StatsServer(8080) // Blocks until process is told to exit via unix signal workers.Run() }
  45. E l i x i r ’s Fe at u

    re s • functional
  46. E l i x i r ’s Fe at u

    re s • functional • concurrency up front
  47. E l i x i r ’s Fe at u

    re s • functional • concurrency up front • fault tolerant (supervisor tree)
  48. E l i x i r ’s Fe at u

    re s • functional • concurrency up front • fault tolerant (supervisor tree) • REPL
  49. E l i x i r ’s Fe at u

    re s • functional • concurrency up front • fault tolerant (supervisor tree) • REPL • great standard library
  50. E l i x i r ’s Fe at u

    re s • functional • concurrency up front • fault tolerant (supervisor tree) • REPL • great standard library • metaprogramming
  51. E l i x i r ’s Fe at u

    re s • functional • concurrency up front • fault tolerant (supervisor tree) • REPL • great standard library • metaprogramming • macros
  52. E l i x i r ’s Fe at u

    re s • functional • concurrency up front • fault tolerant (supervisor tree) • REPL • great standard library • metaprogramming • macros • tooling
  53. f u n c t i o n a l

    i m m u t a b l e d a t a
  54. – M AT Z , T H I S M

    O R N I N G “Shared state is the source of all evil!”
  55. c o n c u r re n c y

    f i r s t a c t o r p a t t e r n
  56. f a u l t t o l e r

    a n t s u p e r v i s o r t re e
  57. defmodule SumsTheThings do def sum(a, b) do do_sum(a, b) end

    defp do_sum(a, b) do a + b end end SumsTheThings.sum(1, 2) #=> 3
  58. defmodule User do defstruct username: nil, email: nil, login_attempts: 0

    end aaron = %User{ username: “pferdefleisch", email: "[email protected]" }
  59. defmodule Shape do def area({:rectangle, w, h}) do w *

    h end def area({:circle, r}) do :math.pi * r * r end end Shape.area({:rectangle, 2, 3}) #=> 6 Shape.area({:circle, 3}) #=> 28.25999999999999801048 Shape.area({:circle, 3, 2}) #=> no function clause matching in Shape.area/1 # examples adopted from learnxinyminutes.com
  60. defmodule Shape do def area({:rectangle, w, h}) do w *

    h end def area({:circle, r}) do :math.pi * r * r end end Shape.area({:rectangle, 2, 3}) #=> 6 Shape.area({:circle, 3}) #=> 28.25999999999999801048 Shape.area({:circle, 3, 2}) #=> no function clause matching in Shape.area/1 # examples adopted from learnxinyminutes.com
  61. defmodule Shape do def area({:rectangle, w, h}) do w *

    h end def area({:circle, r}) do :math.pi * r * r end end Shape.area({:rectangle, 2, 3}) #=> 6 Shape.area({:circle, 3}) #=> 28.25999999999999801048 Shape.area({:circle, 3, 2}) #=> no function clause matching in Shape.area/1 # examples adopted from learnxinyminutes.com
  62. defmodule Shape do def area({:rectangle, w, h}) do w *

    h end def area({:circle, r}) do :math.pi * r * r end end Shape.area({:rectangle, 2, 3}) #=> 6 Shape.area({:circle, 3}) #=> 28.25999999999999801048 Shape.area({:circle, 3, 2}) #=> no function clause matching in Shape.area/1 # examples adopted from learnxinyminutes.com
  63. defmodule Shape do def area({:rectangle, w, h}) do w *

    h end def area({:circle, r}) do :math.pi * r * r end end Shape.area({:rectangle, 2, 3}) #=> 6 Shape.area({:circle, 3}) #=> 28.25999999999999801048 Shape.area({:circle, 3, 2}) #=> no function clause matching in Shape.area/1 # examples adopted from learnxinyminutes.com
  64. defmodule Shape do def area({:rectangle, w, h}) do w *

    h end def area({:circle, r}) do :math.pi * r * r end end Shape.area({:rectangle, 2, 3}) #=> 6 Shape.area({:circle, 3}) #=> 28.25999999999999801048 Shape.area({:circle, 3, 2}) #=> no function clause matching in Shape.area/1 # examples adopted from learnxinyminutes.com
  65. defmodule Shape do def area({:rectangle, w, h}) do w *

    h end def area({:circle, r}) do :math.pi * r * r end end Shape.area({:rectangle, 2, 3}) #=> 6 Shape.area({:circle, 3}) #=> 28.25999999999999801048 Shape.area({:circle, 3, 2}) #=> no function clause matching in Shape.area/1 # examples adopted from learnxinyminutes.com
  66. f a s t c o n c u r

    re n t b e a u t i f u l re l i a b l e
  67. defmodule HelloPhoenix.Router do use HelloPhoenix.Web, :router pipeline :browser do plug

    :accepts, ["html"] plug :fetch_session plug :fetch_flash plug :protect_from_forgery plug :put_secure_browser_headers end pipeline :api do plug :accepts, ["json"] end scope "/", HelloPhoenix do pipe_through :browser # Use the default browser stack get "/", PageController, :index end # Other scopes may use custom stacks. # scope "/api", HelloPhoenix do # pipe_through :api # end end
  68. defmodule HelloPhoenix.Router do use HelloPhoenix.Web, :router pipeline :browser do plug

    :accepts, ["html"] plug :fetch_session plug :fetch_flash plug :protect_from_forgery plug :put_secure_browser_headers end pipeline :api do plug :accepts, ["json"] end scope "/", HelloPhoenix do pipe_through :browser # Use the default browser stack get "/", PageController, :index end # Other scopes may use custom stacks. # scope "/api", HelloPhoenix do # pipe_through :api # end end
  69. defmodule HelloPhoenix.Router do use HelloPhoenix.Web, :router pipeline :browser do plug

    :accepts, ["html"] plug :fetch_session plug :fetch_flash plug :protect_from_forgery plug :put_secure_browser_headers end pipeline :api do plug :accepts, ["json"] end scope "/", HelloPhoenix do pipe_through :browser # Use the default browser stack get "/", PageController, :index end # Other scopes may use custom stacks. # scope "/api", HelloPhoenix do # pipe_through :api # end end
  70. defmodule HelloPhoenix.Router do use HelloPhoenix.Web, :router pipeline :browser do plug

    :accepts, ["html"] plug :fetch_session plug :fetch_flash plug :protect_from_forgery plug :put_secure_browser_headers end pipeline :api do plug :accepts, ["json"] end scope "/", HelloPhoenix do pipe_through :browser # Use the default browser stack get "/", PageController, :index end # Other scopes may use custom stacks. # scope "/api", HelloPhoenix do # pipe_through :api # end end
  71. defmodule HelloPhoenix.Router do use HelloPhoenix.Web, :router pipeline :browser do plug

    :accepts, ["html"] plug :fetch_session plug :fetch_flash plug :protect_from_forgery plug :put_secure_browser_headers end pipeline :api do plug :accepts, ["json"] end scope "/", HelloPhoenix do pipe_through :browser # Use the default browser stack get "/", PageController, :index end # Other scopes may use custom stacks. # scope "/api", HelloPhoenix do # pipe_through :api # end end
  72. defmodule HelloPhoenix.UsersController do use HelloPhoenix.Web, :controller def show(conn, %{"product_id" =>

    product_id}) do product = Repo.get(Product, product_id) render conn, "show.html", product: product end end
  73. defmodule HelloPhoenix.UsersController do use HelloPhoenix.Web, :controller def show(conn, %{"product_id" =>

    product_id}) do product = Repo.get(Product, product_id) render conn, "show.html", product: product end end
  74. defmodule HelloPhoenix.UsersController do use HelloPhoenix.Web, :controller def show(conn, %{"product_id" =>

    product_id}) do product = Repo.get(Product, product_id) render conn, "show.html", product: product end end
  75. defmodule HelloPhoenix.UsersController do use HelloPhoenix.Web, :controller def show(conn, %{"product_id" =>

    product_id}) do product = Repo.get(Product, product_id) render conn, "show.html", product: product end end
  76. S t u ff I D i d n ’

    t H a v e Ti m e Fo r
  77. S t u ff I D i d n ’

    t H a v e Ti m e Fo r • concurrency model
  78. S t u ff I D i d n ’

    t H a v e Ti m e Fo r • concurrency model • text processing
  79. S t u ff I D i d n ’

    t H a v e Ti m e Fo r • concurrency model • text processing • metaprogramming
  80. S t u ff I D i d n ’

    t H a v e Ti m e Fo r • concurrency model • text processing • metaprogramming • error handling
  81. S t u ff I D i d n ’

    t H a v e Ti m e Fo r • concurrency model • text processing • metaprogramming • error handling • macros
  82. S o m e Pro b l e m s

    W i t h Ru b y
  83. S o m e Pro b l e m s

    W i t h Ru b y • performance
  84. S o m e Pro b l e m s

    W i t h Ru b y • performance • most of errors happen at runtime
  85. S o m e Pro b l e m s

    W i t h Ru b y • performance • most of errors happen at runtime • large memory footprint
  86. C r y s t a l ’s G o

    a l s • Have the same syntax as Ruby, or at least as similar as possible.
  87. C r y s t a l ’s G o

    a l s • Have the same syntax as Ruby, or at least as similar as possible. • Never have to specify the type of a variable or method argument.
  88. C r y s t a l ’s G o

    a l s • Have the same syntax as Ruby, or at least as similar as possible. • Never have to specify the type of a variable or method argument. • Be able to call C code by writing bindings to it in Crystal.
  89. C r y s t a l ’s G o

    a l s • Have the same syntax as Ruby, or at least as similar as possible. • Never have to specify the type of a variable or method argument. • Be able to call C code by writing bindings to it in Crystal. • Have compile-time evaluation and generation of code, to avoid boilerplate code.
  90. C r y s t a l ’s G o

    a l s • Have the same syntax as Ruby, or at least as similar as possible. • Never have to specify the type of a variable or method argument. • Be able to call C code by writing bindings to it in Crystal. • Have compile-time evaluation and generation of code, to avoid boilerplate code. • Compile to efficient native code.
  91. S o m e O f C r y s

    t a l ’s Fe at u re s
  92. S o m e O f C r y s

    t a l ’s Fe at u re s • method overloading
  93. S o m e O f C r y s

    t a l ’s Fe at u re s • method overloading • concurrency via channels and spawn
  94. S o m e O f C r y s

    t a l ’s Fe at u re s • method overloading • concurrency via channels and spawn • static typing with type inference
  95. S o m e O f C r y s

    t a l ’s Fe at u re s • method overloading • concurrency via channels and spawn • static typing with type inference • FFI-like simple C bindings
  96. class User def initialize(name) @name = name end def say_my_name

    puts "My name is #{@name}" end end user = User.new("Aaron") user.say_my_name
  97. require "kemal" messages = [] of String sockets = []

    of HTTP::WebSocket public_folder "src/assets" get "/" do render "src/views/index.ecr" end ws "/" do |socket| sockets.push socket socket.on_message do |message| messages.push message sockets.each do |a_socket| a_socket.send messages.to_json end end end
  98. T h a n k Yo u b i t

    . l y / r u b y - a l t e r n a t i v e s @mraaroncruz S l i d e s a n d re s o u rc e s a t :