From: Christian Neukirchen Date: 2007-09-09T00:56:06+09:00 Subject: Re: wishing of reactive programming in ruby ashishwave writes: > ruby integrates power of functional programming from lisp , purest OO > from smalltalk, prototype oriented power from self etc etc into one > single language > > i just was wondering that whether the heavy developers/users of > reactive languages like kanaputs or reactive-C etc will ever get > reactive features in ruby. > > in kanaputs, If the variable 'c' is defined as c = a + b; and if the > reactivity of 'c' is set (c.reactive = true;), then each time the > value of 'a' or 'b' changes then the value of 'c' is updated as the > result of the addition > > > bye :-) > Ashish > ashishwave@yahoo.com. Half-way done ideas wrt that, inspired by flapjax. No warranty. $ cat /Users/chris/mess//2006/49/frp.rb require '/Users/chris/projects/blogcode/dynamic.rb' Thread.abort_on_exception = true Dynamic.variable :current_calc class Var def initialize(value=nil) @affects = [] @value = value end def set!(v) @value = v propagate v end def ~ @affects << Dynamic.current_calc if Dynamic.current_calc @value end def propagate @affects.each { |a| a.call } end def map(init=nil, &block) new = Var.new init Calc.new { new.set! block.call(~self) } new end def inject(initial, &block) a = initial map { |e| a = block.call a, e } end def constant(const) map { const } end def hold(initial, &block) map(initial) { block.call } end def filter(&block) new = Var.new Calc.new { this = ~self if block.call this new.set! this end } new end def merge(other) new = Var.new Calc.new { new.set! ~self } Calc.new { new.set! ~other } new end def calm(window, &block) last = Time.now merge(Timer.for(window)).filter { p [:calm, last, Time.now] r = (Time.now - last) > window last = Time.now r } end end class Calc < Proc attr_reader :timers def initialize(&block) @timers = {} super(&block) Dynamic.let :current_calc => self do call end end end class Timer attr_reader :var def self.for(period) if Dynamic.current_calc.nil? new(period).var else Dynamic.current_calc.timers.fetch(period) { Dynamic.current_calc.timers[period] = new(period) }.var end end def initialize(period) @period = period @var = Var.new Time.now.to_f Thread.new { loop { p "sleeping for #@period" sleep @period @var.set! Time.now.to_f } } end end x = Var.new 5 # Calc.new { p ["at time ", ~Timer.for(0.1), "x is ", ~x] } Calc.new { p ["x is ", ~x] } y = x.map { |z| z * 2 } Calc.new { p ["y is now", ~y] } sum = x.inject(0) { |a,e| a + e } Calc.new { p ["sum is now", ~sum] } Timer.for(5).map { p "tick!" } x.calm(2).map(false) { |v| p "no input for 2 secs!" } # Timer.for(10).merge(x).map { p "either 10s or x" } x.set! 7 while line = gets.to_i x.set! line p ["set to ", ~x] end -- Christian Neukirchen http://chneukirchen.org