Haskellの評価戦略を作ったよ

Haskellの評価を書いたのに基づいて、純粋関数型言語の評価戦略を書いてみました。若干、胡散臭い部分がありますが、少なくとも Wiki に書いたのは動いています。無限リスト(repeat)もちゃんと扱えています。

object Haskell extends Application {
  // 構文に出てくるノード
  abstract class Expr
  case class Symbol(name:String) extends Expr
  case class Lambda(left:List[Symbol], right:List[AnyRef]) extends Expr
  abstract class Statement
  case class Equation(left:List[Symbol], right:List[AnyRef]) extends Statement
  // 短縮表記用の関数
  def S (name:String) = new Symbol(name)
  def L (l:List[Symbol], r:List[AnyRef]) = new Lambda(l, r)
  def EQ(l:List[Symbol], r:List[AnyRef]) = new Equation(l, r)
  // 定義済みの式
  val eqs = List(
      // cons x y = \f -> f x y
      EQ(List(S("cons"), S("x"), S("y")), 
          List(L(List(S("f")), List(S("f"), S("x"), S("y"))))),
      // car  l   = l (\x y -> x)
      EQ(List(S("car"), S("l")),
          List(S("l"), L(List(S("x"), S("y")), List(S("x"))))),
      // repeat x = cons x (repeat x)
      EQ(List(S("repeat"), S("x")),
          List[AnyRef](S("cons"), S("x"), List(S("repeat"), S("x")))))
  
  // car (repeat X) を評価し、X が返ってくる
  println("final = " + eval(List(S("car"), List(S("repeat"), S("X"))))(0))
  // car (cons X Y) を評価し、X が返ってくる
  println("final = " + eval(List(S("car"), List(S("cons"), S("X"), S("Y"))))(0))
  
  // 評価(式変形)を行う
  def eval(e:List[AnyRef]):List[AnyRef] = {
    println("e = " + e)
    // 関数を適用する
    val eq = eqs.find(isMatch(_, e))
    if(eq.isDefined)
      return eval(replace(e, eq.get.left.tail, eq.get.right))
    // ラムダ式を評価する
    e(0) match {
      case lb:Lambda if(e.size >= 2) =>
        return eval(replace(e, lb.left, lb.right))
      case _ =>
    }
    // 引数の評価
    for(i <- 0 until e.length) {
      if(e(i).isInstanceOf[List[AnyRef]]) {
        val ei = eval(e(i).asInstanceOf[List[AnyRef]])
        return eval(List.concat(e.take(i),
            if(ei.size == 1) List(ei(0)) else ei,
            e.drop(i+1)))
      }
    }
    return e
  }
  
  // 式変形
  def replace(expr:List[AnyRef], left:List[Symbol], right:List[AnyRef]):List[AnyRef] = {
    // 変数名 -> 値
    val map = Map(left.zip(expr.tail):_*)
    // 右辺を置換
    def rep(right:List[AnyRef]):List[AnyRef] = 
      right.map(_ match {
        case s:Symbol => 
          if(map.contains(s)) map(s) else s
        case l:List[AnyRef] => 
          rep(l)
        case lb:Lambda =>
          L(rep(lb.left).asInstanceOf[List[Symbol]], rep(lb.right))
      })
    return List.concat(rep(right), expr.drop(left.length + 1))
  }
  
  def isMatch(eq:Equation, expr:List[AnyRef]):boolean =
    eq.left(0) == expr(0) && eq.left.size <= expr.size
}