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

Statyc vs Dynamic: why not both?

Statyc vs Dynamic: why not both?

En esta sesión vamos a peparar la charla que nuestro miembro Mario Camou dará en Berlín el 17 de Agosto, dentro de la Conferencia JRuby europea. Mario nos presentará las nuevas características para llamar Scala de JRuby, así como la introducción de una biblioteca de interoperabilidad denominada Scuby que hemos construido para aliviar el dolor de llamar JRuby desde Scala.

Como se trata de un work in progress, el objetivo es poder ayudar a nuestro compañero aportando ideas, ejemplos y cualquier otra cosa de utilidad para que la presentación berlinesa sea un éxito. Cómo no, el logo de Madrid JUG estará incluído en alguna slide ;-)

* Página del evento en G+: http://is.gd/5u2qkW

MadridJUG

August 09, 2012
Tweet

More Decks by MadridJUG

Other Decks in Programming

Transcript

  1. Agenda • Why? • Why Scala? • Calling Scala from

    JRuby • Calling JRuby from Scala • Q&A
  2. Why? Ruby (and dynamic languages in general) are great •

    Rapid development • Flexibility • Duck-typing • Metaprogramming
  3. 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
  4. 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
  5. 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/
  6. Performance JRuby performance is great and getting better... (and it

    doesn’t matter if the application is waiting for the user 10 times faster)
  7. Performance • For some tasks, static typing is faster •

    Heavy computation • Method lookup • method_missing • Some benchmarks: http://shootout.alioth.debian.org/u32/performance.php
  8. Agenda • Why? • Why Scala? • Calling Scala from

    JRuby • Calling JRuby from Scala • Q&A
  9. Why Scala? • Simplified syntax • Functional programming • Dynamic-language

    features • Mix-ins (traits) • Structural types • Implicits • The Dynamic trait • Scala libraries
  10. 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)
  11. 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
  12. 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”)
  13. 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
  14. 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
  15. 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
  16. 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)
  17. Akka • Based on the Actor model (Erlang) • Message

    passing • Transparent distribution • Messaging system integration • AMQP • Apache Camel • HTTP • ... • Software Transactional Memory
  18. 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!
  19. Agenda • Why? • Why Scala? • Calling Scala from

    JRuby • Calling JRuby from Scala • Q&A
  20. 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 => #<Java::AppHelpers::Foo:0x325bc91> 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
  21. 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 => #<Java::AppHelpers::Foo:0x325bc91> f(1, 2, 3) { |x, y| x + y } => true
  22. 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”
  23. 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
  24. Agenda • Why? • Why Scala? • Calling Scala from

    JRuby • Calling JRuby from Scala • Q&A
  25. 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
  26. 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
  27. 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
  28. 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
  29. Usage • require & eval • Creating objects • Calling

    methods • Convenience methods • Additional facilities
  30. 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 ...
  31. 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
  32. 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("[]")
  33. 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")
  34. 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)
  35. 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")
  36. 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
  37. 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
  38. Future Steps • Scala side • Ruby collections • Ruby

    side • Create a Scuby gem • FunctionN -> block conversion • Scala collections • Object#to_scala
  39. Agenda • Why? • Why Scala? • Calling Scala from

    JRuby • Calling JRuby from Scala • Q&A