Slide 1

Slide 1 text

Static or Dynamic Typing? Why Not Both? Mixing JRuby and Scala Mario Camou @thedoc

Slide 2

Slide 2 text

Agenda • Why? • Why Scala? • Calling Scala from JRuby • Calling JRuby from Scala • Q&A

Slide 3

Slide 3 text

Why?

Slide 4

Slide 4 text

Why? Ruby (and dynamic languages in general) are great • Rapid development • Flexibility • Duck-typing • Metaprogramming

Slide 5

Slide 5 text

Why? ...but there are some pitfalls • Integration • Correctness • Performance • Productivity

Slide 6

Slide 6 text

Integration JRuby gives you access to any Java libraries and frameworks...

Slide 7

Slide 7 text

Integration ...but not all of them are JRuby-friendly (even though JRuby keeps getting better!) • Class names and actual classes (beyond jrubyc) • Method signatures • Overloaded methods • Type erasure in generics • Subclassing • Annotations • Executable JAR files • Legacy applications

Slide 8

Slide 8 text

Integration Use statically-typed proxies to bridge the gap http://www.geekologie.com/2012/03/endless-possibilities-universal-construc.php

Slide 9

Slide 9 text

Correctness • Static analysis • Refactoring • Self-documentation • Type errors • Unit tests can help... • ...but they have to be complete... • ...and they don’t cover all possible scenarios... • ...and tracking down type errors can be Hell http://evanfarrer.blogspot.com.es/2012/06/unit-testing-isnt-enough-you-need.html

Slide 10

Slide 10 text

Correctness • Use a statically-typed language for critical or library code • Use a dynamically-typed language for high- level dynamic code and DSLs http://olabini.com/blog/2008/06/fractal- programming/

Slide 11

Slide 11 text

Performance JRuby performance is great and getting better... (and it doesn’t matter if the application is waiting for the user 10 times faster)

Slide 12

Slide 12 text

Performance • For some tasks, static typing is faster • Heavy computation • Method lookup • method_missing • Some benchmarks: http://shootout.alioth.debian.org/u32/performance.php

Slide 13

Slide 13 text

Performance Implement performance-critical tasks in a compiled statically-typed language

Slide 14

Slide 14 text

Productivity • Refactoring (again!) • Code navigation • IDE help (method parameters, autocomplete, ...)

Slide 15

Slide 15 text

Agenda • Why? • Why Scala? • Calling Scala from JRuby • Calling JRuby from Scala • Q&A

Slide 16

Slide 16 text

Why Scala? • Simplified syntax • Functional programming • Dynamic-language features • Mix-ins (traits) • Structural types • Implicits • The Dynamic trait • Scala libraries

Slide 17

Slide 17 text

Simplified Syntax • Case classes case class Person (firstName:String, lastName:String) • Type inference val m = new HashMap[Int, String] • No getters / setters Unless you really need them • More flexible method names (think DSLs) Use (almost) any character Translated to legal names in bytecode (i.e., + is $plus, += is $plus$eq)

Slide 18

Slide 18 text

Functional Programming Mixed OO - Functional model • Closures / partial functions • foreach, map, fold, filter, ... • Define your own control structures • Immutable eager and lazy values • Pattern matching • For comprehensions

Slide 19

Slide 19 text

Traits • Interfaces with method definitions • Can be used for mix-ins • Calling the previous method in the chain with no aliasing • Dependency injection (“cake pattern”)

Slide 20

Slide 20 text

Structural Types • Declare what you need, not the type • Statically-typed duck typing class Foo { def x = "Foo.x" } class Bar { def x = "Bar.x" } def doIt (arg: { def x:String }) = arg.x scala> doIt(new Foo) res0: String = Foo.x scala> doIt(new Bar) res1: String = Bar.x

Slide 21

Slide 21 text

Implicits • Automatically convert one object to another type • Solve some of the same problems as open classes class MyRichString(str: String) { def acronym = str.toCharArray.foldLeft("") { (t, c) => t + (if (c.isUpperCase) c.toString else "") } } implicit def str2MRString(str: String) = new MyRichString(str) scala> "The HitchHiker's Guide To The Galaxy".acronym res0: java.lang.String = THHGTTG

Slide 22

Slide 22 text

Implicits http://www.codecommit.com/blog/ruby/implicit-conversions-more-powerful-than-dynamic-typing In Ruby: class Fixnum alias_method :__old_lt, '<'.to_sym def <(target) if target.kind_of? String __old_lt__ target.size else __old_lt__ target end end end In Scala: implicit def newLT(i: Int) = new { def <(str: String) = i < str.length } scala> 1 < "foo" res0: Boolean = false scala> 5 < "foo" res1: Boolean = true

Slide 23

Slide 23 text

The Dynamic Trait • Similar to method_missing • Experimental in 2.9, available in 2.10 object Test extends Dynamic { def applyDynamic (method:String) (args: Any*) { println ("%s (%s)".format(method, args.mkString(","))) } } scala> Test.foo("bar",'baz, 1) foo (bar,'baz, 1)

Slide 24

Slide 24 text

Scala Libraries • Akka • Parser combinators • Play / Lift / Scalatra / ...

Slide 25

Slide 25 text

Akka • Based on the Actor model (Erlang) • Message passing • Transparent distribution • Messaging system integration • AMQP • Apache Camel • HTTP • ... • Software Transactional Memory

Slide 26

Slide 26 text

Akka • Mikka: Actors in JRuby by Theo Hultberg (@iconara) • Thin wrapper around Akka Java API to make it more Ruby-like • https://github.com/iconara/mikka • ...for more info ask Theo!

Slide 27

Slide 27 text

Agenda • Why? • Why Scala? • Calling Scala from JRuby • Calling JRuby from Scala • Q&A

Slide 28

Slide 28 text

Calling Scala from JRuby • Just like Java! • JRuby sugar • 1.6.0+ • 1.6.6+

Slide 29

Slide 29 text

Just like Java! In Scala: package app.helpers import scala.reflect.BeanProperty class Foo { private var question: String = "" @BeanProperty var a: String = "" def questionAndAnswer = "Unknowable" def setQ(s:String) = { question = s } def getQ = question } In JRuby: require ‘java’ => true f = Java::app.helpers.Foo.new => # f.q = "Life, the Universe and Everything" => "Life, the Universe and Everything" f.a = "42" => "42" f.q => "Life, the Universe and Everything" f.a => "42" f.question_and_answer => "Unknowable" https://github.com/jruby/jruby/wiki/CallingJavaFromJRuby

Slide 30

Slide 30 text

Just like Java! Closures In Scala: package app.helpers class Foo { def test(x:Int, y:Int, z:Int, f:(Int, Int) => Int) = { f(x,y) == z } } In JRuby: f = Java::app.helpers.Foo.new => # f(1, 2, 3) { |x, y| x + y } => true

Slide 31

Slide 31 text

JRuby Sugar - 1.6.0+ Singleton (Scala object) support Call Singleton methods just like static/class methods In Scala: package app.helpers object Foo { def test = "Static method" } In JRuby: require ‘java’ # => true Java::app.helpers.Foo.test # => “Static method”

Slide 32

Slide 32 text

JRuby Sugar - 1.6.6+ Operator aliases $plus -> + $minus -> - $div -> / $plus$eq -> += apply (a.k.a. ()) -> [] update (a.k.a. ()=) -> []= ... There are some caveats... see https://github.com/jruby/jruby/wiki/Integrating-with-Scala

Slide 33

Slide 33 text

Agenda • Why? • Why Scala? • Calling Scala from JRuby • Calling JRuby from Scala • Q&A

Slide 34

Slide 34 text

Calling JRuby from Scala • JRuby Embed API (RedBridge / JSR-223) • Scuby

Slide 35

Slide 35 text

JRuby Embed API val container = new ScriptingContainer val receiver = container.runScriptlet(""" # Radioactive decay def amount_after_years(q0, t) q0 * Math.exp(1.0 / $half_life * Math.log(1.0/2.0) * t) end def years_to_amount(q0, q) $half_life * (Math.log(q) - Math.log(q0)) / Math.log(1.0/2.0) end """) container.put("$half_life", 24100) // Plutonium val args = Array[Object](10.0:java.lang.Double, 1000:java.lang.Integer) val result = container.callMethod("amount_after_years", args, Double.class) https://github.com/jruby/jruby/wiki/RedBridgeExamples

Slide 36

Slide 36 text

Scuby • Goals • Assumptions and defaults • Usage • Future steps

Slide 37

Slide 37 text

Scuby Goals • Thin DSL layer between Scala and JRuby • Simplify calling JRuby • Calling JRuby should be as transparent as possible • Static typing as far as possible

Slide 38

Slide 38 text

Usage To use Scuby: • Download the artifacts from Maven Central • https://oss.sonatype.org/content/repositories/releases/cc/ abstra/pasilla/scuby/0.1.8/ • Add scuby-0.1.8.jar to your CLASSPATH • Use Maven (or SBT, Gradle, ...) • groupId: cc.abstra.pasilla • artifactId: scuby • Current version: 0.1.8 Add Scuby to your project file

Slide 39

Slide 39 text

Assumptions & Defaults • Single JRuby engine • For our needs, we don’t need more • You don’t have to pass in the engine to every call • Singleton interpreter scope (default) • Otherwise you can get things like nil != nil • Can be changed before first JRuby call • Transient local variable behavior (default) • Local variables don’t survive multiple evaluations • If you need them to persist, store in a Scala val (and pass as parameter)... • ...or change before first JRuby call

Slide 40

Slide 40 text

Usage • require & eval • Creating objects • Calling methods • Convenience methods • Additional facilities

Slide 41

Slide 41 text

Example Ruby File # File test.rb (from the Scuby tests) module Core class Person attr_accessor :firstname, :lastname def initialize (firstname, lastname) @firstname = firstname @lastname = lastname end def fullname "#{firstname} #{lastname}" end def get_label javax.swing.JLabel.new(fullname) end end ...

Slide 42

Slide 42 text

Example Ruby File ... module Backend def self.get_people # Get data from the backend and return an Array of Person end def self.get_data { :people => get_people, :other_data => get_other_data } end def self.get_person(name) # Get a person's data from the DB and return a Person object end def self.get_other_data # Get some other data that is needed for the app end end end

Slide 43

Slide 43 text

require & eval import cc.abstra.scuby.JRuby._ // Require a Ruby file from the classpath require("test") // Eval a Ruby statement discarding the return value eval("import Core") // Eval a Ruby statement that returns a Ruby object val array = eval[RubyObj]("[]") // Or with type inference val array2:RubyObj = eval("[]")

Slide 44

Slide 44 text

Creating Objects import cc.abstra.scuby._ // Create a Ruby object val array3 = new RubyObject('Array) // Create a proxy object for the Ruby BackEnd class val backend = RubyClass('Backend) // Create an instance of the Person class val person = new RubyObject('Person, "Zaphod", "Beeblebrox") val person2 = RubyClass('Person) ! ('new, "Ford", "Prefect")

Slide 45

Slide 45 text

Calling Methods // Call a method on a Ruby object (in this case, the Ruby class), // passing in parameters, and get back another Ruby object val zaphod = backend ! ('get_person, "Zaphod") // Call a Ruby method with no parameters val data = backend ! 'get_data // Ruby method chaining val length = backend ! 'get_people ! 'length // Get a reference to a Ruby method that can later be called val getPerson = backend --> 'get_person // Call the method. Returns an AnyRef. // With the above, these 2 lines are equivalent: getPerson("Zaphod") backend('get_person, "Zaphod") // Call a Ruby method which returns a Java object, // in a type-safe way val label = person.send[JLabel]('get_label)

Slide 46

Slide 46 text

Arrays and Hashes // Access to a Ruby Hash or Array (i.e., anything that implements []) // and creating a Ruby Symbol using % val people = data(%('people)) val zaphod2 = people(0) // Multidimensional Hashes or Arrays (i.e., data["parm1"]["parm2"]) val ford = data(%('people), 1) // Modify/add an element to a Hash or Array (or anything that // implements []=) people(2) = RubyClass('Person) ! ('new, "Arthur", "Dent")

Slide 47

Slide 47 text

Convenience Methods • toString, equals, hashCode • Forwarded to their Ruby equivalents (#to_s, #==, #hash) • respondTo_? array3 respondTo_? 'length // true array3 respondTo_? 'foo // false • isA_? array3 isA_? 'Array // true array3 isA_? 'Hash // false

Slide 48

Slide 48 text

Wrapping in Traits trait Person { def firstname: String def firstname_=(f: String): Unit def lastname: String def lastname_=(l: String): Unit def fullname: String def getLabel: JLabel } val zaphod = backend('get_person, "Zaphod").as[Person] zaphod.firstname = "The Zeeb" println(zaphod.fullname) val label = zaphod.getLabel

Slide 49

Slide 49 text

Future Steps • Scala side • Ruby collections • Ruby side • Create a Scuby gem • FunctionN -> block conversion • Scala collections • Object#to_scala

Slide 50

Slide 50 text

Agenda • Why? • Why Scala? • Calling Scala from JRuby • Calling JRuby from Scala • Q&A

Slide 51

Slide 51 text

Thank you Mario Camou @thedoc http://github.com/abstracc http://github.com/mcamou http://www.abstra.cc Special thanks to @MadridJUG