What to use as metatable? - oop

Reading http://lua-users.org/wiki/LuaClassesWithMetatable the Vector example uses the following technique for the metatable:
Vector={}
Vector_mt={__index=Vector}
function Vector:new(x,y)
return setmetatable({x=x,y=y},Vector_mt)
end
function Vector:add(v)
return Vector:new(self.x+v.x,self.y+v.y)
end
Supposing we want to use __add to support the + operator, we need to explicitly mention it in the metatable, and also we need to reorder things, so that the metatable and the constructor are mentioned after Vector:add:
Vector={}
function Vector:add(v)
return Vector:new(self.x+v.x,self.y+v.y)
end
Vector_mt={__index=Vector,__add=Vector.add}
function Vector:new(x,y)
return setmetatable({x=x,y=y},Vector_mt)
end
To avoiding having to mention each metamethod in the metatable, I can set Vector itself as the metatable, then I can add __add (plus, obviously, __index) as a method of Vector:
Vector={}
function Vector:new(x,y)
return setmetatable({x=x,y=y},Vector)
end
function Vector:add(v)
return Vector:new(self.x+v.x,self.y+v.y)
end
function Vector:__index(k)
return Vector[k]
end
function Vector:__add(b)
return self:add(b)
end
is the latter not recommended and why?

and also we need to reorder things, so that the metatable and the constructor are mentioned after Vector:add
No. A table is a reference. On adding metatable, it is not deep copied. You can add fields to it later, and it will affect the metatable.
This is over-complicated:
function Vector:__index(k)
return Vector[k]
end
Simply do this:
Vector.__index = Vector
See https://codereview.stackexchange.com/a/253022/230923 for an example of a Lua "class".

Related

Lodash way of creating a custom function

I'm trying to implement a simple function in pure lodash way.
function forward(i) => {
return (j) => {
return String.fromCharCode(i + j)
}
}
So that I can do _.range(26).map(forward(65)). Take me some time to make this work:
function a = _.wrap(String.fromCharCode, (fn, a, b) => fn(a + b))
function b = _.ary(a, 2)
function forward = _.curry(b)
Now my question is is there an easier way to do this? and how do I use sum to construct (fn, a, b) => fn(a + b)?
One last thing is I couldn't find wrap function file in Lodash repo.
The function _.curry(...) is kind of strange when it comes to calling functions with various parameters. Let me guide you with an example below.
The ary-function (_.ary(..., 2)) takes any function and ensures its never called with more than a specific amount of arguments (in this case two). Less arguments than specified, will just end up calling the underlying function with less arguments. A definition of this function could look like this:
function ary() {
const args = arguments;
// implementation
}
There is no way to tell how many arguments the function is expecting, as you would with a function with actual parameters (function(a, b) { }). If you would define const forward1 = _.curry(_.ary(target, 2)), and call it with forward1(42)(2), the curry function would just pass down the first argument to ary as it thinks its done.
We can get around this by using an overload of curry that specifies how many parameters the underlying function is expecting (const forward2 = _.curry(target, 2)). Only in the case where forward2 is called in a curry-style (not sure what its even called) with two parameters, it passes it down to target. A call with one argument will just return a new function, waiting for it to be called with the second argument. Now we can get rid of the ary-call, as it serves us no purpose anymore.
As for chaining actions, there's a helper for that. For example: c(b(a(...) can be rewritten to _.flow([a, b, c]). Lodash also provides a function for a + b, which is _.add().
Together your problem can be rewritten to:
const forward = _.curry(_.flow([_.add, String.fromCharCode]), 2);
or more verbose:
const methods = _.flow([
_.add,
String.fromCharCode
]);
const forward = _.curry(methods, 2);
Note that the 2 corresponds to the amount of parameters the _.add method expects.

middleclass: add getter-setter support for properties

I'm trying to add automatic support for property declarations, so that a class gets getters and setters generated automatically for them. I use the middleclass library as a base for classes. I've defined a root class that handles property creation. However, in tests, only an immediate subclass of the root class works properly. Others give me stack overflow error deep inside the middleclass code ([string "local middleclass = {..."]:82: stack overflow).
My code is:
local CBaseObject=class('CObjectBase');
function CBaseObject:initialize()
self._init=true;
end;
function CBaseObject:finalize()
self._init=false;
end;
function CBaseObject:_getter_setter(v)
return v;
end;
function CBaseObject:_gen_prop_cache()
rawset(self,'_properties',rawget(self,'_properties') or {});
end;
function CBaseObject:__index(k)
print('GET',k);
self:_gen_prop_cache();
local prop=self._properties[k];
if prop~=nil
then
local getter=self[prop[2] or '_getter_setter'];
return getter(self,prop[1]);
else return nil;end;
end;
function CBaseObject:__newindex(k,v)
print('ME',self.class.name);
print('SET',k,v);
self:_gen_prop_cache();
local prop=self._properties[k];
if prop==nil and self._init or prop
then
if prop==nil then prop={};self._properties[k]=prop;end;
local vv=prop[1];
if type(v)=='table' and #v<4
then
for i=1,3 do prop[i]=v[i];end;
else
prop[1]=v;
end;
local setter=self[prop[3] or '_getter_setter'];
prop[1]=setter(self,prop[1],vv);
else
rawset(self,k,v);
end;
end;
Test classes:
local cls=CBaseObject:subclass('test');
function cls:initialize()
self.class.super.initialize(self);
self.health={1000,'_gethealth','_sethealth'};
self.ammo=100;
self:finalize();
end;
function cls:_sethealth(value,old)
print('WRITE:',value,old);
if value<0 then return old;else return value;end;
end;
function cls:_gethealth(value)
print('READ:',value);
return value/1000;
end;
local cls2=cls:subclass('test2');
function cls2:initialize()
self.class.super.initialize(self);
self.ammo=200;
self:finalize();
end;
function cls2:_sethealth(value,old)
print('WRITE_OVERRIDEN:',value,old);
return value;
end;
local obj=cls2(); --change this to cls() for working example.
obj.health=100;
obj.health=-100;
print(obj.health,obj._properties.health[1]);
print(obj.ammo,obj._properties.ammo[1]);
I used https://repl.it/languages/lua to run my code. So, the questions is, is what I do even the right approach? Is it possible to add property support in an easier way compatible with the used library? Or should I use another, and what then?
EDIT: after experimenting I found out that the construct self.class.parent.<method>(<...>) is to blame for the error. I replaced all such occurrences by actual parent classes. That was the only problem, it seems, after that the code started to work error-free so far.
Enrique GarcĂ­a Cota (middleclass creator) has enlightened me with what I think is a nice way to implement getters/setters on middleclass.
He suggested to create and use a mixin.
I've done some minor changes while testing/using this mixin recipe. Currently the one I'm using looks like this:
-- properties.lua
local Properties = {}
function Properties:__index(k)
local getter = self.class.__instanceDict["get_" .. k]
if getter ~= nil then
return getter(self)
end
end
function Properties:__newindex(k, v)
local setter = self["set_" .. k]
if setter ~= nil then
setter(self, v)
else
rawset(self, k, v)
end
end
return Properties
You would have to create function get_* and function set_* for your properties (or modify the string pattern above as you wish).
Example:
local class = require('middleclass')
local Properties = require('properties')
local Rect = class('Rect'):include(Properties)
function Rect:initialize(x, y, w, h)
self.x = x
self.y = y
self.w = w
self.h = h
end
function Rect:get_right()
return self.x + self.w
end
function Rect:set_right(right)
self.x = self.x + right - self.right
end
r = Rect:new(10,10, 100, 100)
print(r.right) -- 110
print(r:get_right()) -- 110
r.right = 200
print(r.right) -- 200
print(r.x) -- 100
This way you can use this mixin in whatever classes you want to have properties and simply create the get_* and set_* functions on it.
He also said, however:
I am not a huge fan of getters/setters in Lua. In other languages, I can accept them; for example in ruby, they are integrated in the language's message-passing mechanism.
But in Lua they are extra syntactic sugar, and risk making things more "magical" (unexpected to someone unfamiliar with the code).
Performance note: It's worth mentioning, however, that using __index functions, like the example does, should impact heavily on your code's performance (if compared to __index tables).
Personally, after some time using getters and setters (due to my Python bias), I've decided to write things explicitly and no longer rely on them.
It depends, of course, if performance is critical or not for your code.

Can I dynamically call a local function?

I have a module that looks like this. I've simplified it greatly so as not to clutter up my question (my local functions are more complex than this). Here is my code:
decision = {}
function win(win_arg) return win_arg end
function lose(lose_arg) return lose_arg end
local function draw(draw_arg) return draw_arg end
function decision.get_decision(func, arg)
return func(arg)
end
return decision
I am calling my module with the code below.
my = require "my-lua-script"
print(my.get_decision(lose, "I lose."))
print(my.get_decision(win, "I win."))
I want 'get_decision' to be a public method. I want win, lose and draw to be private, but I want to call them dynamically from get_decision. If I understand correctly, win and lose are in the global namespace right now? If I put a local in front of these two methods (like draw) then my code doesn't work.
Is there a way to accomplish what I am after?
Of course.
my-script.lua
-- This is the local side of the module.
local outcomes = {}
function outcomes.win(arg) return arg end
function outcomes.lose(arg) return arg end
function outcomes.draw(arg) return arg end
-- This is the exposed side of the module.
local M = {}
function M.get_decision(outcome, arg)
return outcomes[outcome](arg)
end
return M
main.lua
local my = require'my-script'
print(my.get_decision('win', 'I win.'))
print(my.get_decision('lose', 'I lose.'))
You simply use a string to indicate which function you'd like to access, and use that string to index a table of functions from within get_decision against outcomes. This will keep the functions hidden behind get_decision.

Trying to exit a method, using return, but doesn't actually return anything

I'm using return EXIT_SUCCESS to exit a void function. However I get a flag that says my method should not return a value.
The code works fine, but is there a better way to exit a method by using a return or something else?
To add to the other answers, you can always return from a void function with return;
Redefine the method as
-(int)myMethod;
Then you can return EXIT_SUCCESS.
Is any code calling this method and expecting a value?
I don't think you understand how functions work. When you specify the prototype asvoid you are saying the function won't return anything. You are then contradicting the prototype by trying to return something. Change the prototype to -(int)function to return EXIT_SUCCESS. If you do not want to return something, but want to still check if it was successful you could pass in a pointer to the function: -(void)functionReturnValue:int* returnValue. Make the function set returnValue to EXIT_SUCCESS on successful completion and have the calling function check returnValue.

List.ForEach in vb.net - perplexing me

Consider the following code example:
TempList.ForEach(Function(obj)
obj.Deleted = True
End Function)
And this one:
TempList.ForEach(Function(obj) obj.Deleted = True)
I would expect the results to be the same, however the second code example does NOT change the objects in the list TempList.
This post is more to understand why...? Or at least get some help understanding why...
It's because you used Function instead of Sub. Since a Function returns a value, the compiler considers that the equals sign (=) is used as a comparison, not an assignment. If you change Function to Sub, the compiler would correctly consider the equals sign as an assignment:
TempList.ForEach(Sub(obj) obj.Deleted = True)
If you had a multiline lambda; you wouldn't have had this problem:
TempList.ForEach(Function(obj)
obj.Deleted = True
Return True
End Function)
Obviously, for the ForEach method it makes no sense to use a Function because the return value wouldn't be used, so you should use a Sub.