Is it possible to replicate Ruby's method_missing in Lua? - syntax

I'm fairly sure that in Lua, you can use a given metatable's __index, __newindex, and __call to roughly replicate Ruby's method_missing. And I somewhat have:
function method_missing(selfs, func)
local meta = getmetatable(selfs)
local f
if meta then
f = meta.__index
else
meta = {}
f = rawget
end
meta.__index = function(self, name)
local v = f(self, name)
if v then
return v
end
local metahack = {
__call = function(self, ...)
return func(selfs, name, ...)
end
}
return setmetatable({}, metahack)
end
setmetatable(selfs, meta)
end
_G:method_missing(function(self, name, ...)
if name=="test_print" then
print("Oh my lord, it's method missing!", ...)
end
end)
test_print("I like me some method_missing abuse!")
print(this_should_be_nil)
My problem is this: While the syntax is similar, and I can certainly use it to replicate the functionality, it introduces a breaking error. Every single variable that you use in the context of the table you apply a method_missing to is never nil, since I have to return an object that can be called in order to pass the buck of the potential call from the index function to an actual call.
i.e. After defining a global method_missing as above, attempting to call undefined method 'test_print' runs as expected, but the value of test_print when indexed is non-nil, and other methods/variables that aren't responded to, like this_should_be_nil are non-nil.
So is it possible to avoid this pitfall? Or can the syntax not be bent to support this modification without modifying the language source itself? I imagine the difficulty arises in how in Ruby, indexing and calling are analogous, whereas in Lua they are distinct.

You can avoid this problem by making nil value callable.
Unfortunatelly, this can be done only from host code (i.e., C program), not from Lua script.
Pascal code:
function set_metatable_for_any_value_function(L: Plua_State): Integer; cdecl;
begin // set_metatable_for_any_value(any_value, mt)
lua_setmetatable(L, -2);
Result := 0;
end;
procedure Test_Proc;
var
L: Plua_State;
const
Script =
'set_metatable_for_any_value(nil, ' +
' { ' +
' __call = function() ' +
' print "This method is under construction" ' +
' end ' +
' } ' +
') ' +
'print(nonexisting_method == nil) ' +
'nonexisting_method() ';
begin
L := luaL_newstate;
luaL_openlibs(L);
lua_pushcfunction(L, lua_CFunction(#set_metatable_for_any_value_function));
lua_setglobal(L, 'set_metatable_for_any_value');
luaL_dostring(L, Script);
lua_close(L);
end;
Output:
true
This method is under construction

You have identified the problem well: it is not, as far as I know, possible to solve that issue in pure Lua.
EDIT: I was wrong, you can by making nil callable. See other answers. It is still a bad idea IMO. The main use case for method_missing is proxy objects and you can solve that in another way. method_missing on Kernel (Ruby) / _G (Lua) is terrible :)
What you could do is only handle some methods, for instance if you know you expect methods that start by test_:
local function is_handled(method_name)
return method_name:sub(1,5) == "test_"
end
function method_missing(selfs, func)
local meta = getmetatable(selfs)
local f
if meta then
f = meta.__index
else
meta = {}
f = rawget
end
meta.__index = function(self, name)
local v = f(self, name)
if v then
return v
end
if is_handled(name) then
local metahack = {
__call = function(self, ...)
return func(selfs, name, ...)
end
}
return setmetatable({}, metahack)
end
end
setmetatable(selfs, meta)
end
_G:method_missing(function(self, name, ...)
if name=="test_print" then
print("Oh my lord, it's method missing!", ...)
end
end)
test_print("I like me some method_missing abuse!")
print(this_should_be_nil)
Now maybe the question should be: why do you want to replicate method_missing, and can you avoid it? Even in Ruby it is advised to avoid the use of method_missing and prefer dynamic method generation when possible.

So with the tip from #lhf, I've managed a passable double (from what I can tell) of method_missing. In the end, I developed the following:
local field = '__method__missing'
function method_missing(selfs, func)
local meta = getmetatable(selfs)
local f
if meta then
f = meta.__index
else
meta = {}
f = rawget
end
meta.__index = function(self, name)
local v = f(self, name)
if v then
return v
end
rawget(self, name)[field] = function(...)
return func(self, name, ...)
end
end
setmetatable(selfs, meta)
end
debug.setmetatable(nil, { __call = function(self, ...)
if self[field] then
return self[field](...)
end
return nil
end, __index = function(self, name)
if name~=field then error("attempt to index a nil value") end
return getmetatable(self)[field]
end, __newindex = function(self, name, value)
if name~=field then error("attempt to index a nil value") end
getmetatable(self)[field] = value
end} )
_G:method_missing(function(self, name, ...)
local args = {...}
if name=="test_print" then
print("Oh my lord, it's method missing!", ...)
return
elseif args[1] and string.find(name, args[1]) then --If the first argument is in the name called...
table.remove(args, 1)
return unpack(args)
end
end)
test_print("I like me some method_missing abuse!")
test_print("Do it again!")
print(test_print, "method_missing magic!")
print(this_should_be_nil == nil, this_should_be_nil() == nil)
print(conditional_runs("runs", "conditionally", "due to args"))
print(conditional_runs("While this does nothing!")) --Apparently this doesn't print 'nil'... why?
Output:
Oh my lord, it's method missing! I like me some method_missing abuse!
Oh my lord, it's method missing! Do it again!
nil method_missing magic!
true true
conditionally due to args
This snippet lets you use method_missing pretty similarly to how you can in Ruby (with no response checking whatsoever, though). It's similar to my initial response, except it 'passes the buck' through nil's metatable, something I thought I couldn't do. (thanks for the tip!) But as #greatwolf says, there is probably no reason to ever use a construct like this in Lua; the same dynamism can probably be achieved through more clear metamethod manipulations.

Related

ruby lambda capture : a weird effect and workaround

closures = []
vals = ('a'..'z').to_a
until vals.empty?
val = vals.shift()
closures << lambda { puts val }
end
closures.each { |l| l.call() }
this Ruby code prints 'z' for every call which is a bit surprising
def closure(val)
lambda {puts val}
end
closures = []
vals = ('a'..'z').to_a
until vals.empty?
val = vals.shift()
closures << closure(val)
end
closures.each { |l| l.call() }
this prints 'a' to 'z' as it would be expected.
so what I see here is certain misbehavior in Ruby lambdas capturing their parameters at the moment of their creation
can please anyone explain this effect by citing Ruby specification ? (my Ruby is 2.2.5p319/Cygwin)
should this be reported as a bug in Ruby bug tracker ?
or it is an expected behavior ?
or it has already been fixed in some further version of Ruby ?
thanks in advance for your replies
UPDATE. Here is the same code ported to Perl. Surprisingly, but it works as expected:
use strict;
use warnings;
my #vals = 'a'..'z';
my #subs = ();
while (#vals) {
my $val = shift #vals;
push #subs, sub { print "$val\n"; };
}
$_->() for #subs;
Variables are captured by reference in Ruby, not by value (the same thing is true in Python, JavaScript, and many other languages). Furthermore, the scope of val is the function scope, not scoped to the inside of the loop, so you don't get a new variable val in each iteration of the loop -- it's the same variable val; you are just assigning another value to it in each iteration.
In each iteration of the loop, a closure is created that references the variable val -- the exact same variable val. Thus, when the closures are evaluated later, they all read the same value -- the value of the (single) variable val at that point.
When you pass it to a method and create the closure inside the method, it's different because the variable that the closure captures is the val in the body of the method closure, scoped to that method. Each time you call the method closure, you get a new variable val, whose value is the value passed in, and it is never altered thereafter (nothing in closure assigns to it). So when the value is read by the closure later, it is still the value passed in to the call of closure when the closure was created.
I believe what is happening here is in the first case
closures = []
vals = ('a'..'z').to_a
until vals.empty?
val = vals.shift()
closures << lambda { puts val }
end
closures.each { |l| l.call() }
Every time you push lambda { puts val } into closures, you are simply pushing in a method that does not remember the current value of val. So, if we add the line puts val at the end of the until loop, val = 'z', so when you call each lambda in the closure, you are calling puts val, with the current value of val.
In the second case,
def closure(val)
lambda {puts val}
end
closures = []
vals = ('a'..'z').to_a
until vals.empty?
val = vals.shift()
closures << closure(val)
end
closures.each { |l| l.call() }
When you push closure(val) into closures, ruby is able to remember the current value of the argument, so you are pushing closure('a'), closure('b'), etc. Now, when you call each l in closures, you are able to print out a to z.

Behaviour of tap for recursive functions

I like to use tap occasionally as a beautifier for method returns. However, when using tap with a recursive function, it's behavior is different than what I expect:
class Node
attr_accessor :name, :children
def initialize(name); self.name, self.children = name, []; end
def render
res = "Name: #{name}\n"
children.each do |child|
res += " - " + child.render + "\n"
end
res
end
end
parent = Node.new('Parent')
parent.children = [Node.new('Child')]
puts parent.render
Returns
Name: Parent
- Name: Child
If I change the render function to use tap:
def render
"Name: #{name}\n".tap do |res|
children.each do |child|
res += " - " + child.render + "\n"
end
end
end
It returns
Name: Parent
I would assume the behavior would be identical to the first render function. The docs indicate it "Yields x to the block, and then returns x"...since the function is recursing is it somehow polluting the function stack?
This has nothing to do with anything except that assignment changes a variable, and variables are passed by value. tap is irrelevant, the behavior is identical if you place a string into any variable.
In your case, you're passing a string literal into a proc, which receives a variable called res containing a copy of that string. You're then modifying that variable, not the original string itself.
Consider:
def test(res)
res += "bar"
end
x = "foo"
test(x)
puts x # outputs "foo", not "foobar"
The reason your first example works is that you're replacing the value in the string res with a new value. You're not actually appending data to the string stored in res.

Traversal of binary tree in ruby

I'm writing a recursive toString method in a binary tree class in Ruby. The algorithm uses two methods as shown below. I have a local variable called result, which is to hold the concatenation of all the values in the tree. For some odd reason, when I concatenate result in the toString algorithm, the root of the tree changes.
def toString
currentNode = #root
result = "<"
if nil != currentNode
result << (getString currentNode, result)
end
result = result + ">"
return result
end
def getString(currentNode, result)
if currentNode != nil
result = currentNode.value.toString
if nil != currentNode.left
result << "," # Through debugging, I have noticed that it starts here!!!!
result.concat (self.getString currentNode.left, result)
end
if nil != currentNode.right
result << ","
result.concat (self.getString currentNode.right, result)
end
end
return result
end
So, where I have noted in the comment above, the value at the root of the tree begins to change. Instead of concatenating with result, it concatenates with the root, which changes the tree, instead of simply traversing and concatenating the values. I have used << and concat. Same result. Could someone please help shed some light on this? I am fairly new to Ruby. Thank you. :)
Contrary to other languages (like java or c#) Strings in ruby are not immutable (by default). This means that if currentNode.value.toString returns the actual string value of the value (and not a copy of it) calling << on it will change the value itself:
class Dummy
attr_accessor :value
end
dummy = Dummy.new
dummy.value = 'test'
result = dummy.value
result << 'something else'
# => "testsomething else"
dummy.value
# => "testsomething else" # !!!!
To avoid that you need to make sure you return a duplicate of the value, and not the value itself.
dummy.value = 'test'
result = dummy.value.dup
result << 'something else'
# => "testsomething else"
dummy.value
# => "test" # :-)

Lua function syntax sugar for a digit started name

Lua 5.1.4
For example:
bar = {}
bar.name = 'test'
bar['123.com'] = function(self) print(self.name) end
I can't call the method like below:
bar:['123.com']()
stdin:1: '<name>' expected near '['
Althought below works:
bar['123.com'](bar)
But I this is somehow ugly. Is there a syntax sugar for this situation?
Or if it really cannot do it, will Lua team add this syntax in future?
Or they did this intentionally?
No we can not call the method like you want. Your suppose to call the method as following syntax only.
bar['123.com'](bar)
Just make an alias that doesn't begin with numbers and use that.
bar.name123 = bar['123name']
bar:name123()
Nobody knows what the Lua maintainers will add in future versions (they're pretty close-mouthed), but my guess would be that it's unlikely they'll add it without at least being asked—and I've never seen a request for such a thing on the Lua mailing list...
My intuition, though, is that this functionality seems obscure: how often do people really want to call methods with "weird" names like that? Given that, and that the "workaround" really isn't bad at all, it's unclear whether it's worth adding complexity to the implementation to support such a feature (unless it's completely trivial).
If you want to try asking, you should post about it to the Lua mailing list, which is where most such discussion takes place: lua-l#lists.lua.org
You might want to consider something like below (you can test online at http://www.lua.org/cgi-bin/demo):
bar = {}
bar.name = 'test'
bar['123.com'] = function(self) print(self.name) end
bar2 = setmetatable({}, {
__index = function(t, key)
return function(...)
return bar[key](bar, ...)
end
end
})
bar2['123.com']()
-- output: test
You can also change bar itself to behave in a similar way, only you must do it before you assign any values - e.g.:
bar = {}
local privatekey = {}
setmetatable(bar, {
__index = function(t, key)
local value = rawget(t, privatekey)[key]
if type(value) ~= 'function' then
return value
end
return function(...)
return value(t, ...)
end
end,
__newindex = function(t, key, value)
rawset(t, privatekey, rawget(t, privatekey) or {})
rawget(t, privatekey)[key] = value
end
})
bar.name = 'test'
bar['123.com'] = function(self) print(self.name) end
bar['123.com']()
-- output: test
You can use a closure:
bar = {}
bar.name = 'test'
bar['123.com'] = function() print(bar.name) end
bar['123.com']()
function bar:addBar(name)
bar[name] = function() print(self.name) end
end
bar:addBar('456.com')
bar['456.com']()
Oh god...
bar = {
['123.com'] = function(self)
print("YAY!", self)
end
}
function f()
print(bar)
bar:WHATTHE()
end
function crazy(f, patt, repl)
local f_str = string.dump(f)
local newf_str = string.gsub(f_str, "WHATTHE", "123.com")
assert(#newf_str == #f_str, "replacement must be of same length")
local newf = loadstring(newf_str)
setfenv(newf, getfenv(f))
return newf
end
f = crazy(f, "WHATTHE", "123.com")
f()
--[[ Output:
table: 005EB688
YAY! table: 005EB688
]]
Don't do this - ever.

Passing a method as a parameter in Ruby

I am trying to mess around a little bit with Ruby. Therefor I try to implement the algorithms (given in Python) from the book "Programming Collective Intelligence" Ruby.
In chapter 8 the author passes a method a as parameter. This seems to work in Python but not in Ruby.
I have here the method
def gaussian(dist, sigma=10.0)
foo
end
and want to call this with another method
def weightedknn(data, vec1, k = 5, weightf = gaussian)
foo
weight = weightf(dist)
foo
end
All I got is an error
ArgumentError: wrong number of arguments (0 for 1)
The comments referring to blocks and Procs are correct in that they are more usual in Ruby. But you can pass a method if you want. You call method to get the method and .call to call it:
def weightedknn( data, vec1, k = 5, weightf = method(:gaussian) )
...
weight = weightf.call( dist )
...
end
You want a proc object:
gaussian = Proc.new do |dist, *args|
sigma = args.first || 10.0
...
end
def weightedknn(data, vec1, k = 5, weightf = gaussian)
...
weight = weightf.call(dist)
...
end
Just note that you can't set a default argument in a block declaration like that. So you need to use a splat and setup the default in the proc code itself.
Or, depending on your scope of all this, it may be easier to pass in a method name instead.
def weightedknn(data, vec1, k = 5, weightf = :gaussian)
...
weight = self.send(weightf)
...
end
In this case you are just calling a method that is defined on an object rather than passing in a complete chunk of code. Depending on how you structure this you may need replace self.send with object_that_has_the_these_math_methods.send
Last but not least, you can hang a block off the method.
def weightedknn(data, vec1, k = 5)
...
weight =
if block_given?
yield(dist)
else
gaussian.call(dist)
end
end
...
end
weightedknn(foo, bar) do |dist|
# square the dist
dist * dist
end
But it sounds like you would like more reusable chunks of code here.
You can pass a method as parameter with method(:function) way. Below is a very simple example:
def double(a)
return a * 2
end
=> nil
def method_with_function_as_param( callback, number)
callback.call(number)
end
=> nil
method_with_function_as_param( method(:double) , 10 )
=> 20
The normal Ruby way to do this is to use a block.
So it would be something like:
def weightedknn(data, vec1, k = 5)
foo
weight = yield(dist)
foo
end
And used like:
weightedknn(data, vec1) { |dist| gaussian( dist ) }
This pattern is used extensively in Ruby.
You can use the & operator on the Method instance of your method to convert the method to a block.
Example:
def foo(arg)
p arg
end
def bar(&block)
p 'bar'
block.call('foo')
end
bar(&method(:foo))
More details at http://weblog.raganwald.com/2008/06/what-does-do-when-used-as-unary.html
You have to call the method "call" of the function object:
weight = weightf.call( dist )
EDIT: as explained in the comments, this approach is wrong. It would work if you're using Procs instead of normal functions.
I would recommend to use ampersand to have an access to named blocks within a function. Following the recommendations given in this article you can write something like this (this is a real scrap from my working program):
# Returns a valid hash for html form select element, combined of all entities
# for the given +model+, where only id and name attributes are taken as
# values and keys correspondingly. Provide block returning boolean if you
# need to select only specific entities.
#
# * *Args* :
# - +model+ -> ORM interface for specific entities'
# - +&cond+ -> block {|x| boolean}, filtering entities upon iterations
# * *Returns* :
# - hash of {entity.id => entity.name}
#
def make_select_list( model, &cond )
cond ||= proc { true } # cond defaults to proc { true }
# Entities filtered by cond, followed by filtration by (id, name)
model.all.map do |x|
cond.( x ) ? { x.id => x.name } : {}
end.reduce Hash.new do |memo, e| memo.merge( e ) end
end
Afterwerds, you can call this function like this:
#contests = make_select_list Contest do |contest|
logged_admin? or contest.organizer == #current_user
end
If you don't need to filter your selection, you simply omit the block:
#categories = make_select_list( Category ) # selects all categories
So much for the power of Ruby blocks.
Similarly to a Proc or a method call, you can also pass a lambda as weightf parameter :
def main
gaussian = -> (params) {
...
}
weightedknn(data, vec1, k = 5, gaussian, params)
# Use symbol :gaussian if method exists instead
end
def weightedknn(data, vec1, k = 5, weightf, params)
...
weight = weightf.call(params)
...
end
you also can use "eval", and pass the method as a string argument, and then simply eval it in the other method.

Resources