if式オーバーロード

注意

本物のif式ではありません。
if式っぽいもの。

概要

動的型付け言語では、if式(条件式)がいろいろな型について使える。
(数値型、オブジェクト型、など)
それを静的型付けのScalaでやってみる。

実装

オーバーロードといえば型クラス。
Scalaで型クラスといえばtraitへのimplicit conversion
(+今回必要なかったけどview bound)らしい。

package myif

trait Condition {
  def condition : Boolean
}

object IfTest {
  def If[T](a : Condition) = new If[T](a)
  
  def main(args : Array[String]) : Unit = {
    //Either
    val right = If(Right("right")) Then 100 Else 200
    val left = If(Left("left")) Then 100 Else 200
    //Option
    val some = If(Some("some")) Then 100 Else 200
    val none = If(None) Then 100 Else 200
    //Int
    val one = If(1) Then 100 Else 200
    val zero = If(0) Then 100 Else 200
    //Double
    val oned = If(1.0) Then 100 Else 200
    val zerod = If(0.0) Then 100 Else 200

    //mixin is OK
    val testObj = new Object with Condition {
      override def condition = true
    }
    val obj = If(testObj) Then 100 Else 200
    
    //Boolean
    //only hoge is printed
    If(true) Then { println("hoge") } Else { println("fuga") } 
        
    println(right)
    println(left)
    println(some)
    println(none)
    println(one)
    println(zero)
    println(oned)
    println(zerod)
    println(obj)
    
    //odd use
    val thenthen = If(true) Then 100 Then 200
    println(thenthen Else 300)
    println(thenthen Else 400)

  }
  
  implicit def boolean_condition(b : Boolean) : Condition = {
    new Condition {
      override def condition = b
    }
  }
  
  implicit def option_condition[T] (x : Option[T]) : Condition = {
    new Condition{
      override def condition = !x.isEmpty
    }
  }
  
  implicit def either_condition[T, U](x : Either[T, U]) : Condition = {
    new Condition{
      override def condition = x.isRight
    }
  }
  
  implicit def int_condition(n : Int) : Condition = {
    new Condition{
      override def condition = n != 0
    }
  }
  
  implicit def double_condition(d : Double) : Condition = {
    new Condition{
      override def condition = d != 0
    }
  }
}

class If[T](cond : Condition) {
  var truecase : () => T = null
  var falsecase : () => T = null
  
  def Then(truecase : => T) : If[T] = {
    this.truecase = () => truecase
    this
  }
  
  def Else(falsecase : => T) : T = {
    this.falsecase = () => falsecase
    if(cond.condition){
      this.truecase()
    } else {
      this.falsecase()
    } 
  }
}

作り方

  • 条件になるオブジェクトの抽象クラス(traitにした)Conditionを用意する。

trueになるかfalseになるかを表すconditionフィールドを定義しておく。

  • implicit conversionを用意。ここではBoolean, Option, Either, Int, Doubleなど。
  • If式はいろいろな型を返せるので

型パラメータTをもたせたクラスIfを用意する。

  • 構文がそれっぽく見えるようにThenとElseを遅延評価で実装し、

Ifをグローバルな関数にしておく。
Scalaにはthenがないがあった方がよい気がする。
try-catchと同じ要領。
http://d.hatena.ne.jp/kya-zinc/20090722/1259845362

動作

  • 使われないブランチは評価されない。
  • implicit conversionを使わずとも、ミックスインで

条件になるオブジェクトを増やせることを確認。(testObj)

  • ThenでIfインスタンスが返ってくるので怪しい使い方ができる。(thenthen)