Scalaの能力
では、RubyをはじめとしたLLが持っている動的な性質について、Scalaがどの程度実現できるか見てみましょう。

このプログラムは、CarというクラスとEmployeeというクラスがname属性を共通して持っていますが、継承関係にありません。また、Carはprice、Employeeはsalaryを持っているので、それらの値をオブジェクトからcostというメッセージで取得できるように後付けでメソッド追加を行いたいと思います。
クラスの定義だけならScalaの方では簡潔に書けるようです(内容によっては当然Rubyの方が楽なケースも多くあります)。
RubyもScalaもクラスのオブジェクトを格納する変数に型を指定していません。Rubyは動的に処理されるため、変数には本当に型がありません。ところがscalaでは、コンパイラによって使われている型が推論されてつけられます。
2つのクラスで同じメソッド(ここではname)を呼ぶためには、Javaなどではインターフェースや抽象クラスの使用が必要ですが、rubyでは変数に型がないためオブジェクトにメッセージを投げて、メソッドがあれば反応します(Duck typing)。
一方Scalaでは、下記のように「nameメソッドを持ったクラス」のオブジェクトならprintNameメソッドを使うことができる、という文法になっています。これにより、Duck Typing風のことを実現しています。
メソッドの追加に関してはRubyの方が数段柔軟です。実行時のオブジェクトに動的にメソッド追加なども朝飯前です。Scalaでは、逆にimplicit Conversion(暗黙の型変換)のテクニックを使います。下記の例でいくと、
implicit def priceToCost(in: {def price: Int}) = CostHolder(in.price)
という記述をしておくと、costメソッドが必要な時だけCostHolderが動くイメージになります(つまりpriceメソッドの内容が返却される)という感じになります。
class Car(var name:String,val price:Int); // Carクラスの定義 class Employee(var name:String, var salary:Int); // Employeeクラスの定義 case class CostHolder(cost: Int) // costメソッドを持ったcaseクラス // priceメソッドをもったクラスにcostメソッド追加したように動かす implicit def priceToCost(in: {def price: Int}) = CostHolder(in.price) // salaryメソッドを持ったクラスにcostメソッド追加したように動かす implicit def salaryToCost(in: {def salary: Int}) = CostHolder(in.salary) // nameメソッドを持ったクラスのnameメソッドを表示 def printName(x:{def name:String}) = println(x.name) var legacy = new Car("legacy", 3000000) // 型推論 var yamada = new Employee("yamada", 200000) printName(legacy) // Duck Typing風 println("price:"+legacy.price) // 通常のメッセージ println("cost:"+legacy.cost) // メソッドの後付け printName(yamada) println("salary:" + yamada.salary) println("cost:" + yamada.cost)
class Car # Carクラスの定義 attr_accessor :name, :price def initialize(name, price) @name = name @price = price end end class Employee # Employeeクラスの定義 attr_accessor :name, :salary def initialize(name, salary) @name = name @salary = salary end end class Car # Carクラスにcostメソッドを後付け def cost @price end end legacy = Car.new("legacy", 3000000) yamada = Employee.new("yamada", 200000) #Employeeクラスのオブジェクトにcostメソッドを後付け yamada.instance_eval "def cost\n @salary \nend" puts legacy.name # Duck Typing puts legacy.price # 通常のメッセージ puts legacy.cost # メソッドの後付け puts yamada.name puts yamada.salary puts yamada.cost # 実行時にメソッドを後付け
実際にScalaをコーディングしてみると、「う~ん。なかなかええ感じやけど、さすがにRubyに比べると面倒やなぁ…」という感想でした。
「これやったら、Rubyでできるんやから、Rubyから乗り換える必要ないなぁ」と正直思ったのですが、当時の自分は間違っていました。Scalaの比較対象はRubyではないのです!
