「実装して理解する遅延評価の仕組み 〜 thunkを絵に描いて理解しよう」をRubyで

実装して理解する遅延評価の仕組み 〜 thunkを絵に描いて理解しよう・JavaScriptでHaskellを実装!?Rubyで写経してみます。

  • Thunk、 Lambda、App、Evaluateクラスは instance_of? により型を調べるためのものです。
  • Thunk でくるんである以外は lambda から取り出すだけ。
class Thunk
  attr_accessor :value
  def initialize(value)
    @value = value
  end
end

class Lambda
  attr_accessor :fn
  def initialize(fn)
    @fn = fn
  end
end

class App
  attr_accessor :fn, :val
  def initialize(f, v)
    @fn  = f
    @val = v
  end
end

# apply はAppオブジェクトを作ります。
# apply(fn).call(val) : apply は関数を引数にとり、val を引数としてcallされます。
def apply (fn) 
  lambda{|val| Thunk.new( lambda{ App.new(fn, val)} ) } 
end


class Evaluate
  attr_accessor :fn, :val
  def initialize(fn, val)
    @fn  = fn
    @val = val
  end
end

def evaluate(val) 
  while val.instance_of?(Thunk) 
    val = val.value.call
    if val.instance_of?(App)
      val = peelLambda(evaluate(val.fn)).call(val.val)
    end
  end
  val
end

def peelLambda (lam)
  unless lam.instance_of?(Lambda) 
    raise "type error: apply a non-lambda to a value"
  end
  lam.fn
end
y = Thunk.new(lambda{20})
x = Thunk.new(lambda{y})

p evaluate(x)  # => 20

add = Lambda.new(lambda{|x| Lambda.new(lambda{|y| Thunk.new(lambda{ evaluate(x) + evaluate(y)})})} )

sub = Lambda.new(lambda{|x| Lambda.new(lambda{|y| Thunk.new(lambda{ evaluate(x) - evaluate(y)})})} )

one = Thunk.new(lambda{1})
two = Thunk.new(lambda{2})

onetwo = Thunk.new(lambda{ apply( apply(add).call(one)).call(two) })
twoone = Thunk.new(lambda{ apply( apply(sub).call(one)).call(two) })

p evaluate(onetwo) # => 3
p evaluate(twoone) # => -1
class Cons
  attr_accessor :fn, :val
  def initialize(car, cdr)
    @car = car
    @cdr = cdr
  end
end

class Nil
  attr_reader :nil
  def initialize()
    @nil
  end
end


# []
nil_ =  Thunk.new(lambda{Nil.new()})

# (:)
cons = Lambda.new(lambda{|x| Lambda.new( lambda{|xs| Thunk.new( lambda{ Cons.new(x, xs)}) }) })

zero = Thunk.new(lambda{ 0 })

# let x = 0 : x
x = Thunk.new(lambda{ Cons.new(zero, x) })

print evaluate(x)   # => #<Cons:0x59ebd6c>