More than 1 year has passed since last update.

僕はもともとRubyが好きで3年くらい書いており、Scalaはまだ始めて3ヶ月くらいなのですが、今回はそんなRubyistから見たScalaについて書こうかと思います。

Rubyはかなり自由な言語で、あらゆること(メタプログラミングを含む)が簡単にできるようになっているのですが、もしかして頑張ればScalaでもできるのでは!?と思いここ最近調べてみました。

なお、今回の内容は以前Kawasaki.rbで発表した以下のスライドがベースとなっています。
http://www.slideshare.net/yutomatsukubo/rubyistscala

オープンクラス

Rubyにはオープンクラスがあり、既存のクラスを簡単に拡張することができます。

# 既存のStringクラスを拡張
class String
  def add_scala
    self + "scala"
  end
end

puts "ruby is like ".add_scala # => ruby is like scala

Scalaでこれを実現する場合、拡張メソッドを使います。

値クラスと汎用トレイト
http://docs.scala-lang.org/ja/overviews/core/value-classes.html

object MyApp extends App {
  implicit class MyString(val s: String) extends AnyVal {
    def addScala = s + "Scala"
  }

  println("Ruby is like ".addScala) // => "Ruby is like Scala"
}

Rubyに比べると大分おまじない要素が増えたように見えますが、なんとこれだけの記述で実現することができてしまいました。

なお、Rubyでは既存のメソッドすらオーバーライドできてしまいますが、

puts "Ruby".size # => 4

class String
  def size
    -1
  end
end

puts "Ruby".size # => -1

さすがにScalaではそこまでサポートはされていませんでした。(できても困るが)

object MyApp extends App {
  implicit class MyString(val s: String) extends AnyVal {
    override def size: Int = -10
  }

  println("Scala".size)
  // Note that implicit conversions are not applicable because they are ambiguous
  // both method augmentString in object Predef of type (x: String)scala.collection.immutable.StringOps
  // and method MyString in object MyApp of type (s: String)MyApp.MyString
  // are possible conversion functions from String("Scala") to ?{def size: ?}
}

method_missing

Rubyには、存在しないメソッドを呼び出した時の挙動を指定できるmethod_missingメソッドがあります。

method_missing
http://ref.xaio.jp/ruby/method_missing

#!/usr/bin/env ruby

class MyClass
  def foo
    "foo"
  end

  def method_missing(name)
    "#{name} is missing!!"
  end
end

my = MyClass.new
puts my.foo # => "foo"
puts my.bar # => "bar is missing!!"

Scalaでこれを実現するにはDynamicトレイトが使えます。

Dynamic
http://www.scala-lang.org/api/current/index.html#scala.Dynamic

import scala.language.dynamics

class MyClass extends Dynamic {
  def foo = "foo"

  def selectDynamic(name: String): String = s"${name} is missing!"
}

object MyApp extends App {
  val my = new MyClass
  println(my.foo) // => foo
  println(my.bar) // => bar is missing!
}

静的言語でこれが可能というのは正直驚きでした。

ダックタイピング

Rubyは動的型付け言語なので、型が適切かの判断はメソッドが呼べるかどうか(ダックタイピング)で決まります。
ScalaではStructural Subtypingを使えばこれが実現できます。

// hogeメソッドを持つクラス
class MyClass {
  def hoge: String = "hoge!"
}

// hogeメソッドを持たないクラス
class MyClass2 {
  def piyo: String = "piyo!"
}

object MyApp extends App {
  // hogeメソッドを持つ型を定義
  type Hoge = {
    def hoge: String
  }

  // Hogeタイプを受け取るメソッド
  def addFuga(t: Hoge): String = {
    t.hoge + "fuga!"
  }

  val my = new MyClass
  println(addFuga(my)) // => "hoge!fuga!"

  val my2 = new MyClass2
  println(addFuga(my2)) // => コンパイルエラー: type mismatch;
}

かなり頑張る形になりましたが、MyClassはhogeメソッドを持つだけでコンパイルが通り、MyClass2はコンパイルが通りません。

どういった用途でこの機能を使うのかは今のところ僕にはわかっていません。

おわりに

Scalaは静的型付けと関数型プログラミング言語のイメージがあり、硬い言語なのだろうと思っていたのですが、意外に動的で自由な一面を持つ面白い言語だということに気づけました。

Rubyistとしては、今後は是非メタプログラミングの発展も願っています!