kmizuの日記

プログラミングや形式言語に関係のあることを書いたり書かなかったり。

ゆの in Scala

ゆの in Ruby

ゆの in Python

ゆの in Smalltalk

ゆの in Squeak Smalltalk

その他多数

元ネタについてはよく知らんのだけど、なんか流行ってるっぽいので書いてみた。ポイントは、式の中での_がplace holderになってしまうので、通常のパラメータにはならないのをどう解決するかという所だが、implicit conversionを使うことで解決している。

object X {
  implicit val f :((X.type) => String) => Int = x => { println(x(X)); 0 }
  def /(x: X.type) = new {
    def /(x: X.type) = new {
      override def toString() = {
        "スケッチ"
      }
      def <(x: String): String = X + this.toString + "365 " + x
    }
  }

  override def toString(): String = "ひだまり"

  def main(args: Array[String]) {
    (main:Int)
  }

  def main: X.type => String = {
    X / _ / X < "来週も見てくださいね!"
  }
}

追記:

とこれだけだとなんなので、これが一体何をやってるのか、もうちょっと詳しく説明してみることにする。

まず、

object X { ... }

で、Xがオブジェクトとして使えるように=式の中で値として使えるようにしている。

次に、

def /(x: X.type) = new { ... }

だが、これは、Xに対して、2項の/演算子を定義している。このとき、引数の型がX.typeとなっているが、これは、Xと同じインスタンス(であることが保証されている)しか代入できない型、という意味。実は必要無いのだけど、使ってみたかっただけという。あと、

new { ... }

Javaにおける匿名クラスみたいなもので、無名のクラスの新しいインスタンスを生成することができる。継承元のクラスを明記していないが、省略した場合、AnyRefを継承元として指定したのと同じことになる。

X / _ / X < "来週も見てくださいね!"

は、_がなければ、単なる演算子呼び出し(=メソッド呼び出し)のチェインだが、実は、式の中で現れる_は無名関数の仮引数への参照のplace holderとなっており、そのため、上記の式は、

(x) => X / x / X < "来週も見てくださいね!"

のシンタックスシュガーとなる。

さて、ここで問題なのは、

X / _ / X < "来週も見てくださいね"

という式が無名関数を返す式になってしまうため、文字列を出力という副作用を起こすことができないという点だが、これは、最初に書いたように、implicit conversionによって無理やり副作用を挿入する事によって解決した(単純に、main()から返って来た無名関数を呼び出しても良いのだけど、それだとScalaっぽさがあまり出ないし、面白みが無い)。具体的には、mainから返って来る無名関数の型はX.type => Stringになるが、main:Intによって、Int型に「アップキャスト」することで、X.type => StringからIntへのimplicit conversionが挿入されるようにした(型はIntじゃなくても、適当な型ならなんでも良かったのだが、とりあえず一番お手軽なIntを選んだ)。