Lazy loading variables
Greetings! It would be nice to have a fallback for undefined variables, like that:
calculator.on_missing_var do |var|
...
end
The primary use case is circular dependencies - parameters for an expression may be results of evaluating another one. Here is an example:
class A
def initialize
@expressions = {}
end
def override(method, expression)
@expressions[method] = expression
end
def self.dentaku(name, &block)
define_method(name) do
if((exp = @expressions[name]))
c = Dentaku::Calculator.new
c.store("one", one)
c.store("two", two)
c.store("three", three)
c.evaluate(exp)
else
instance_exec(&block)
end
end
end
dentaku(:one) do
1
end
dentaku(:two) do
2
end
dentaku(:three) do
3
end
end
a = A.new
puts a.three
a.override(:three, "one + two")
puts a.three
This will result in "stack level too deep" error. What i would like to do is the following:
def self.dentaku(name, &block)
define_method(name) do
if((exp = @expressions[name]))
c = Dentaku::Calculator.new
c.on_missing_var do |var|
raise "Unknown variable #{var}" unless ["one", "two", "three"].include?(var)
send(var)
end
c.evaluate(exp)
else
instance_exec(&block)
end
end
end
This code can result in the same error too, but only with certain input, which is fine.
I've had a look at the source code, but still can't figure out where to start.
Thanks in advance.
I'm not maintainer here, but curious. It's not clear how you want to implement dependency between variables here? Just lazy load already exists in Dentaku, you can pass lambda as variable value and it's executed each time variable is evaluated. You can add to here local caching and that's your solution:
c = Dentaku::Calculator.new
c_cache = {}
c.evaluate!("a + b + a + b", a: ->{c_cache[:a] ||= p(1)}, b: -> { p(2) })
1
2
2
=> 6
Indeed, that does the job. I couldn't find it in docs though, but could have guessed.
The code can be fixed this way:
require "dentaku"
class A
def initialize
@expressions = {}
end
def override(method, expression)
@expressions[method] = expression
end
def self.dentaku(name, &block)
define_method(name) do
if((exp = @expressions[name]))
c = Dentaku::Calculator.new
c.evaluate(exp, one: -> { one }, two: -> { two }, three: -> { three })
else
instance_exec(&block)
end
end
end
dentaku(:one) do
1
end
dentaku(:two) do
2
end
dentaku(:three) do
3
end
end
a = A.new
puts a.three
a.override(:three, "one + two")
puts a.three