kotlin for rubyists: A JOURNEY

who am i

IVO anjo

Ruby

PERFORMANCE

JRuby

working @ amazon profiler team

(my opinions only)

CONCURRENCY

JVM

KOTLIN

what to expect?

INTRODUCTION

• What is Kotlin?

• Why is it interesting for Rubyists?

• What is it good for?

GETTING KOTLIN

• Hello world service

what to expect?

PROGRAMMING KOTLIN

KOTLIN TOOLING

LANGUAGE NICETIES

what to expect?

KOTLIN NATIVE

CLOSING THOUGHTS

KOTLIN FOR JAVASCRIPT

• React example

objectives

Hands-on introduction to Kotlin

Starting point for your Kotlin discovery

objectives

Motivate why I believe that
Kotlin is
an acceptable Ruby

➜ Please do interrupt me with questions! 👍

Birds-eye tour of Kotlin ecosystem

INTRODUCTION

whAT IS

?

Object-oriented

Statically typed

...programming language

Functional

Open-source

Cross-platform

Created by JetBrains (IntelliJ, RubyMine)

Introduced in 2011

Officially supported by Google for Android

why

at a

conference!?

Story time...

𝓘 𝓼𝓮𝓮 𝓡𝓾𝓫𝔂'𝓼

𝓲𝓷 𝓚𝓸𝓽𝓵𝓲𝓷

𝓹𝓻𝓸𝓰𝓻𝓪𝓶𝓶𝓮𝓻 𝓱𝓪𝓹𝓹𝓲𝓷𝓮𝓼𝓼

In one sentence...

Concise Do a lot with a very small amount of code

Expressive Convey your intent clearly

Pragmatic Reuses familiar concepts and proven solutions from other languages

Safe Design prevents common errors

Interoperable Easy to introduce into existing Java and Javascript codebases

what is

?

Good for

Run Kotlin on the Java Virtual Machine

Run Kotlin on the Java Virtual Machine

Fast!

• Production just-in-time compiler

• State-of-the-art garbage collection

• Performance comparable to Java code

Run Kotlin on the Java Virtual Machine

Massively parallel!

• True parallelism

Async/reactive programming

• Production-ready parallel libraries

Run Kotlin on the Java Virtual Machine

Make use of the awesome Java tooling!

Profilers

Monitoring

Debuggers

}

in production 🚀

Run Kotlin on the Java Virtual Machine

Interoperate with Java code

• Reuse existing high-quality
open-source frameworks and libraries

Web

Database

Messaging

Servers

Scientific

Concurrency

Desktop UIs

Graphics

🌈

💫

Run Kotlin on the Java Virtual Machine

Interoperate with other JVM languages

Scala, Clojure, JRuby, ...

• Yes, you can call Ruby code and gems!

Build Android applications using Kotlin

Android Announces Support for Kotlin

17 May 2017 , By Mike Cleron, Director, Android Platform
 

Today the Android team is excited to announce that we are officially adding support for the Kotlin programming language.

Kotlin is a brilliantly designed, mature language that we believe will make Android development faster and more fun.

Transpile Kotlin to Javascript

Transpile Kotlin to Javascript

Share code between back-end & front-end

Run on Browsers and on Node.js

Use Kotlin native to build native executables

Use Kotlin native to build native executables

Build self-contained applications

Build iOS/macOS native applications

Make use of C, C++, Swift, Objective-C, ... libraries

𝓼𝓾𝓶𝓶𝓲𝓷𝓰 𝓲𝓽 𝓾𝓹

𝓼𝓾𝓶𝓶𝓲𝓷𝓰 𝓲𝓽 𝓾𝓹

Built to target & share code between many platforms

Programmer happiness

Easy to introduce on existing Java/JVM projects

𝓼𝓾𝓶𝓶𝓲𝓷𝓰 𝓲𝓽 𝓾𝓹

Many options for deployment
(including single-file)

Great performance and run-time tooling

Mature ecosystem

Q&A

getting kotlin

sdkman ➜ https://sdkman.io

sdkman ➜ https://sdkman.io

$ curl -s "https://get.sdkman.io" | bash
$ source "$HOME/.sdkman/bin/sdkman-init.sh"
$ sdk version
SDKMAN 5.7.3+337

#1 Get sdkman

$ sdk install kotlin
Downloading: kotlin 1.3.21
In progress...
##################################### 100,0%
Installing: kotlin 1.3.21
Done installing!
Setting kotlin 1.3.21 as default.

#2 Get Kotlin

$ sdk install java
Downloading: java 11.0.2-open
In progress...
##################################### 100,0%
Repackaging Java 11.0.2-open...
Done repackaging...
Installing: java 11.0.2-open
Done installing!
Setting java 11.0.2-open as default.

#3 Get Java VM

$ sdk install kscript
$ sdk install gradle
$ sdk install visualvm

#4 Get extra niceties (we'll get around)

kotlinc: Kotlin's REPL (similar to irb/pry)

#{demo}

kscript: Easily run Kotlin code snippets without needing to compile them first

#{demo}

#!/usr/bin/env kscript

val conf = "Ruby on Ice"
println("Hello everyone at $conf!")
kscript-hello.kts
$ kscript kscript-hello.kts 
Hello everyone at Ruby on Ice!

Running it:

kscript tips

• Can pull dependencies (see docs)

• Can generate IntelliJ project

Kotlin Playground: Online REPL and pastebin

Koans example

IntelliJ IDEA: If you prefer a full IDE

➜ Community edition is free!

Let's do something a bit more interesting...
Bootstrap a new web service using the micronaut framework

$ sdk install micronaut
$ mn --version
| Micronaut Version: 1.0.4
| JVM Version: 1.8.0_191

#{demo}

q&A

PROGRAMMING KOTLIN

LET's look at a few kotlin basics

String templating

var conference = "Ruby on Ice"
println("Hello world from ${conference} ${java.time.Year.now()}")
conference = "Ruby on Ice"
puts "Hello world from #{conference} #{Time.now.year}"

HASHES / MAPS

var placesWeAreAt = mapOf(
    "Tegernsee" to true,
    "The Moon" to false
)
placesWeAreAt.forEach { (place, areWeThere) ->
    println("Are we at $place? $areWeThere")
}
places_we_are_at = {
  "Tegernsee" => true,
  "The Moon" => false
}.freeze
places_we_are_at.each { |place, are_we_there|
  puts "Are we at #{place}? #{are_we_there}"
}
var placesWeAreAt = mapOf(
    "Tegernsee" to true,
    "The Moon" to false
)
placesWeAreAt.forEach { (place, areWeThere) ->
    println("Are we at $place? $areWeThere")
}
places_we_are_at = {
  "Tegernsee" => true,
  "The Moon" => false
}
places_we_are_at.each { |place, are_we_there|
  puts "Are we at #{place}? #{are_we_there}"
}

Four space indentation

camelCaseEverywhere

😱

METHODS/FUNCTIONS

fun giveMe(number: Int, str: String): String {
    return str.repeat(number)
}

giveMe(5, "hello")
def give_me(number, str)
  str * number
end  

give_me(5, "hello")

...with BLOCKS

fun myTimes(howMany: Int, block: (Int) -> Unit) {
    var current = 0
    while (current < howMany) {
        block.invoke(current)
        current += 1
    }
}

myTimes(10) { n -> println("Yay $n!") }
def my_times(how_many, &block)
  current = 0
  while (current < how_many)
    block.call(current)
    current += 1
  end
end

my_times(10) { |n| puts("Yay #{n}!") }

namespace + classes

package onice

class Foo(
    val arg1: String = "immutable arg default", /* default for arg1 */
    var arg2: String = "mutable arg default",
    var arg3: String // No default
) {
    fun concatenateAll(): String { return listOf(arg1, arg2, arg3).joinToString("; ") }
}

println(Foo(arg3 = "arg3").concatenateAll())
module OnIce
  class Foo
    attr_reader   :arg1
    attr_accessor :arg2
    attr_accessor :arg3

    def initialize(arg1: "immutable arg default", arg2: "mutable arg default", arg3:)
      @arg1, @arg2, @arg3 = arg1, arg2, arg3
    end

    def concatenate_all; [arg1, arg2, arg3].join("; "); end
  end
end

puts OnIce::Foo.new(arg3: "arg3").concatenate_all

LAMBDAS

val greeter = { str: String -> "Welcome, $str" }

greeter("Sun!")
greeter = lambda { |str| "Welcome, #{str}" }

greeter.("Sun!")

CONTROL FLOW

val isEven =
    if (number % 2 == 0) {
        "is even"
    } else {
        "is not even"
    }
is_even =
  if (number.even?)
    "is even"
  else
    "is not even"
  end

CONTROL FLOW II

val result = when (obj) {
    1 -> "one"
    "two" -> "two"
    else -> "have no idea"
}
result = case obj
when 1
  "one"
when "two"
  "two"
else
  "have no idea"
end

Safe navigation and "elvis"

val nullString: String? = null
val niceString = "nice!"
val safeCall = nullString?.contains("Hello")
val string = nullString ?: niceString
nil_string = nil
nice_string = "nice!"
safe_call = nil_string&.include?("Hello")
string = nil_string || nice_string

RANGES, SPLATTING

val numbers = (1..10).toList().toTypedArray()

fun many_arguments(vararg args: Any) {
    println(args.toList())
}

many_arguments(*numbers)
numbers = (1..10).to_a

def many_arguments(*args)
  puts args
end

many_arguments(*numbers)

et cetera...

Kotlin's basic constructs are usually
very familiar

I'll leave a few more links to learning resources at the end of the workshop

q&A

KOTLIN tooling

Gradle: Scriptable build tool

Compile

Test

Manage dependencies

• Similar to Bundler+Rake

• Similar to Elixir's Mix

Gradle: Scriptable build tool

Very powerful build tool, handling

• Kotlin, Java, Scala, Clojure, Android

C, C++, Objective-C, Swift

...

Gradle: Scriptable build tool

But also very complex, so be on the look out

Gradle: Scriptable build tool

Our test microservice automatically generated a gradle configuration:

build.gradle

#{demo}

Gradle: Scriptable build tool

DSL in build.gradle uses the groovy programming language

Gradle: Scriptable build tool

Modern versions of gradle allow build.gradle.kts to be written in Kotlin!

#{demo}

Downside: Many docs/examples still use groovy

Gradle: Scriptable build tool

Can also be used to bootstrap

#{demo}

new Kotlin applications

new Kotlin libraries

let's talk testing

Did you know?

RSpec is a meta-gem, which depends on the
rspec-core, rspec-expectations and rspec-mocks gems

RSpec is awesome! *ahem*

Test DSL

Spek framework ➜ rspec-core

Assertions

AssertJ ➜ rspec-expectations

Mocks

mockito-kotlin ➜ rspec-mocks

interface User {
    fun name(): String
}

class RubyOnIce {
    fun hello(): String { return "Hello, world!" }
    fun hello(user: User): String { return "Hello there, ${user.name()}!" }
}

object RubyOnIceTest : Spek({
    val subject by memoized { RubyOnIce() }

    describe("hello") {
        it("greets everyone") {
            assertThat(subject.hello()).isEqualTo("Hello, world!")
        }

        context("when a user is supplied") {
            val name = "Ivo"
            val user = mock<User> { on { name() } doReturn(name) }

            it("greets only the supplied user") {
                assertThat(subject.hello(user)).isEqualTo("Hello there, Ivo!")
            }
        }
    }
})

Running tests:

./gradlew test

• Test results are cached and by default only tests that are affected by changed
code execute

• Silent output (unless a test fails)

• println is silent by default too

Running tests:

./gradlew test

Example setup tweaks for tweaking all these behaviors

performance

debugging

TOOLING

JVisualVM: Metrics, debugging, etc...

• Free

• Powerful plug-ins!

JVisualVM: Metrics, debugging, etc...

• Connect to local and remote VMs

#{demo}

ssh, heroku, ...

Be careful when using in production (see introduction!)

Java Flight Recorder:

Plane-like black-box for your applications

JFR continuously record stats into circular buffer (in production!)

Problem: VisualVM only records data while connected to application

Java Flight Recorder:

Plane-like black-box for your applications

Low-overhead, safe for production use

Can also include custom metrics from your own code

Java Mission Control:
Browse Java Flight Recorder recordings

#{demo}

Other interesting tools:

YourKit Java Profiler

JProfiler

IntelliJ IDEA:
Debug local or remote application

#{demo}

q&A

PROGRAMMING KOTLIN II

DSL EXAMPLE

kotlinx.html library

createHTML().html {
    head {
        title { +"Hello there!" }
    }
    body {
        ul {
            li { +"One item" }
            li { +"Another item" }
        }
    }
}

• Valid Kotlin code

• Still typesafe!

#{demo}

language niceties

Type system: Nullable types

 String? vs String

Enforced check/safe navigation

val maybeNullString: String? = "foo"
println(maybeNullString.length)

example.kts:25:24: error: only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String?
println(maybeNullString.length)
                       ^

Instead of NullPointerError / NoMethodError

Data classes: Less boilerplate for entities

class Foo
  attr_reader :ruby_on_ice
  attr_reader :conference

  def initialize(ruby_on_ice:, conference:)
    @ruby_on_ice = ruby_on_ice
    @conference = conference
  end

  def ==(other)
    ruby_on_ice == other.ruby_on_ice &&
      conference == other.conference
  end

  def to_s
    "Foo ruby_on_ice=#{ruby_on_ice} ..."
  end
end

data class(
  val rubyOnIce: String,
  val conference: String
)

Inline Classes: Type system magic

def check(user, password)
  # ...
end

user = "ivo"
password = "trustno1"

check(password, user)

Oh, I know, types will save me!

Inline Classes: Type system magic

fun check(user: String, password: String) {
  // ...
}

val user = "ivo"
val password = "trustno1"

check(password, user)

Yes, yes, classes...

Inline Classes: Type system magic

data class User(val s: String)
data class Password(val s: String)

fun check(user: User, password: Password) {
  // ...
}

val user = User("ivo")
val password = Password("trustno1")

check(password, user)

does not compile! 👌

Inline Classes: Type system magic

inline class User(val s: String)
inline class Password(val s: String)

fun check(user: User, password: Password) {
  // ...
}

val user = User("ivo")
val password = Password("trustno1")

check(password, user)

zero overhead! 👍

Coroutines: Simple concurrency!

import kotlinx.coroutines.*

fun main() {
    GlobalScope.launch {
        delay(1000L)
        println("World!")
    }
    println("Hello,")
    Thread.sleep(2000L)
}

q&A

KOTLIN for JAVASCRIPT

Javascript hello world!

#{demo}

create-react-kotlin-app:

Bootstrap Kotlin + react app easily

#{demo}

q&A

KOTLIN NATIVE

Native hello world!

#{demo}

q&A

CONCLUSION

Using Kotlin is *almost* as fun as Ruby 😁

Should you rewrite your Ruby/Rails codebase in Kotlin?

Most probably not!

Using Kotlin is *almost* as fun as Ruby 😁

Very useful in many use-cases where Ruby is limited or awkward to use

Explore and have fun!

Kotlin can learn from Ruby

Ruby can learn from Kotlin

• Learn and try something different!

• We've only scratched the surface!

References & Recommended reading

Kotlin for Rubyists: A Journey

By Ivo Anjo

Kotlin for Rubyists: A Journey

Slides for the "Kotlin for Rubyists: A Journey" workshop, presented at Ruby on Ice 2019

  • 3,452