Is there a point to getters and setters in Lua?

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
User avatar
Dattorz
Citizen
Posts: 66
Joined: Sat Oct 27, 2012 12:32 am
Contact:

Is there a point to getters and setters in Lua?

Post by Dattorz »

Right now I'm doing some massive API reworking to my LOVE-based game engine, Klua. This project has the first Lua code I ever wrote and it's quite ugly, thus I am revising a large portion of it.

One of the things I'm looking to change is the use of thin getter/setter functions. I think at the time I was implementing them I was still thinking like a C++ programmer. Basically a few of my class source files are full of functions that look like this:

Code: Select all

function entity.Entity:getAngle()
    return self._angle
end

function entity.Entity:setAngle(angle)
    self._angle = angle
end
Obviously, these functions are more or less pointless since one can access self._angle directly, even outside of the class functions. Ignoring this, the getter/setter functions could at least be used to implement type checking, right? I decided to run a (rather artificial) benchmark test:

Code A:

Code: Select all

local blah = { x = 0 }

for i = 0, 999999999 do
    blah.x = i
end

Code: Select all

[aura typecheckspeedtest]$ time luajit typecheck.lua 

real    0m1.018s
user    0m1.017s
sys     0m0.000s
[aura typecheckspeedtest]$ time lua5.1 typecheck.lua 

real    0m40.431s
user    0m40.377s
sys     0m0.013s
Code B:

Code: Select all

local blah = { x = 0 }

function blah:setX(newX)
    if type(newX) ~= "number" then
        error("Invalid type")
    end
    
    self.x = newX
end

for i = 0, 999999999 do
    blah:setX(i)
end

Code: Select all

[aura typecheckspeedtest]$ time luajit typecheck.lua 

real    0m1.019s
user    0m1.017s
sys     0m0.000s
[aura typecheckspeedtest]$ time lua5.1 typecheck.lua 

real    2m36.141s
user    2m36.060s
sys     0m0.003s
LuaJIT seems to handle the type checks really well. Both versions of the code run just as fast. The standard Lua interpreter, on the other hand, takes almost four times as long to run the code with the getter function and type check. (Of course, the standard Lua interpreter takes forty times longer to run Code A than LuaJIT does...)

Let's just say we don't use getters/setters and set the variable directly, bypassing any sort of sanity check... like, say we set x to "pig". The Lua runtime will eventually catch the error, but it will be in some other part of the program, as opposed to a type check catching the error right away.

When it comes to Lua, which is more important, speed and simplicity, or sanity checks?
Last edited by Dattorz on Mon Jun 17, 2013 2:50 am, edited 1 time in total.
User avatar
Inny
Party member
Posts: 652
Joined: Fri Jan 30, 2009 3:41 am
Location: New York

Re: Is there a point to getters and setters?

Post by Inny »

In computer science, there's a property known as Encapsulation, which means that the data contained in an object is properly hidden away, so that if details on the data are changed in the future, the dependent code doesn't also need changing to compensate.

For instance, lets say you store the movement vector of a sprite as deltaX and deltaY, and you access those properties a lot. Then you decide that it's just not working out, and you really want to store them as an angle and magnitude. Other code that uses the deltaX and deltaY would need changing. However, if you put them behind a setter/getter, then you only need to modify the functions which return those values.

At least, that's the dream. I prefer to completely avoid the need for setters and getters by avoiding the need to dig into an object's properties. If there's something you want an object to do, then that should be a method of the object, and other code beyond that object shouldn't be accessing the object's internals. Think of it this way: objects contain verbs, and you tell the object what to do with the verbs.
User avatar
Dattorz
Citizen
Posts: 66
Joined: Sat Oct 27, 2012 12:32 am
Contact:

Re: Is there a point to getters and setters?

Post by Dattorz »

I understand the point of encapsulation. I'm just questioning whether there's any reason to make getter/setter functions since AFAIK Lua doesn't provide much in the way of proper encapsulation - anyone can directly access the variable regardless. It's not like C++ or Java where private variables exist in the language standard and are enforced.
User avatar
ejmr
Party member
Posts: 302
Joined: Fri Jun 01, 2012 7:45 am
Location: South Carolina, U.S.A.
Contact:

Re: Is there a point to getters and setters in Lua?

Post by ejmr »

Dattorz wrote:I'm just questioning whether there's any reason to make getter/setter functions….
There are situations where getters and setters are useful. For example, if setting the background image property of an object should have side-effects that update the display on screen.
Dattorz wrote:…AFAIK Lua doesn't provide much in the way of proper encapsulation - anyone can directly access the variable regardless. It's not like C++ or Java where private variables exist in the language standard and are enforced.
You can make variables private by taking advantage of metatables, changing environments, defining them as local to a specific module, and so on. However, in my personal experience this is rarely necessary. I have been using Lua for years and in the past eighteen months in has been the programming language I’ve used most. Strict type checking is a feature I find valuable, but its absence has not caused any serious problems for me. Looking at a project of mine with three-thousand lines of code reveals only sixteen uses of the type() function. Methodical documentation and testing allows me to write the majority of my code without worrying about types. So to answer the question from your first post: simplicity is the most important. You’ve seen the impact that rampant type checking can have on performance. Therefore I suggest writing the most simple code necessary without worrying about types. Every program demands a different amount of type checking but my own experience has led me to the conclusion that you’re in better off in Lua by beginning with no type checking and then introducing it only as needed. What constitutes ‘as needed’ for your project is a balance that you will discover over time.
ejmr :: Programming and Game-Dev Blog, GitHub
南無妙法蓮華經
User avatar
bartbes
Sex machine
Posts: 4946
Joined: Fri Aug 29, 2008 10:35 am
Location: The Netherlands
Contact:

Re: Is there a point to getters and setters in Lua?

Post by bartbes »

Dattorz wrote: Let's just say we don't use getters/setters and set the variable directly, bypassing any sort of sanity check... like, say we set x to "pig". The Lua runtime will eventually catch the error, but it will be in some other part of the program, as opposed to a type check catching the error right away.
This is always a tricky one, that said, I think a "sane" solution would be:
If this will cause a problem within this function call (say nested calls or whatever), leave it be.
If this will cause a problem in an unrelated piece of code at a later date, error now.

That said, I hardly ever end up checking things, but then I mostly interact with my own code, I know I can read my own code to figure out the real problem. One of the things you can easily check for, though, is nil.

Also, with regards to your test results, I think this only holds true because your sample code is jit-compiled, meaning the type-checks can be optimized out because it already worked out the types, and even if that wasn't the case, jitted the check is probably really simple, too. However, I don't know if the performance loss would still be negligible if it would run on the interpreter instead.
User avatar
Plu
Inner party member
Posts: 722
Joined: Fri Mar 15, 2013 9:36 pm

Re: Is there a point to getters and setters in Lua?

Post by Plu »

There are situations where getters and setters are useful. For example, if setting the background image property of an object should have side-effects that update the display on screen.
This is why I use setters as well; if I have a suspicion the code's going to have side effects from just changing a variable. I never type-check anything though.
User avatar
Xgoff
Party member
Posts: 211
Joined: Fri Nov 19, 2010 4:20 am

Re: Is there a point to getters and setters in Lua?

Post by Xgoff »

"basic" getters/setters seem kind of pointless imo, at least in a language where you can intercept field access. usually if i want something like this and i'm not being lazy, i'd rather implement attributes/properties/whatever they're called this week... ok so they're still getters/setters but they look like regular field access. whatever.

however this is probably going to be even slower than explicit getters/setters because of the extra __index and __newindex magic you'll have to do
User avatar
Inny
Party member
Posts: 652
Joined: Fri Jan 30, 2009 3:41 am
Location: New York

Re: Is there a point to getters and setters?

Post by Inny »

Dattorz wrote:I understand the point of encapsulation. I'm just questioning whether there's any reason to make getter/setter functions since AFAIK Lua doesn't provide much in the way of proper encapsulation - anyone can directly access the variable regardless. It's not like C++ or Java where private variables exist in the language standard and are enforced.
In Lua, Python, Javascript, and other dynamic languages without proper encapsulation support, they take the stance that "We're all adults here", and adopt the convention that you don't access what's considered private. That's why you'll see some properties with names like _foo and _bar, where the underscore is the first character as the "no touching" signal.

I'll reiterate, it's better to redesign your methods to be verbs rather than accessors. To make this advice a little more concrete, lemme show two examples, Accessors first:

Code: Select all

Object = { _foo = 1, _bar = 1 }
function Object:setFoo(x) self._foo = x end
function Object:getFoo(x) return self._foo end
function Object:setBar(x) self._bar = x end
function Object:getBar(x) return self._bar end

function love.update(dt)
  Object:setFoo(Object:getFoo()+1)
  Object:setBoo(Object:getBar()-1)
end
As Verbs next:

Code: Select all

Object = {}
function Object:update()
  self._foo = self._foo + 1
  self._bar = self._bar - 1
end

function love.update(dt)
  Object:update()
end
User avatar
raidho36
Party member
Posts: 2063
Joined: Mon Jun 17, 2013 12:00 pm

Re: Is there a point to getters and setters in Lua?

Post by raidho36 »

You can actually make your Lua objects' properties private, via closure OO emulaton approach.

Code: Select all

local function MyClass(init)
  -- the new instance
  local self = {
    -- public fields go in the instance table
    public_field = 0
  }

  -- private fields are implemented using locals
  -- they are faster than table access, and are truly private, so the code that uses your class can't get them
  local private_field = init

  function self.foo()
    return self.public_field + private_field
  end

  function self.bar()
    private_field = private_field + 1
  end

  -- return the instance
  return self
end
http://lua-users.org/wiki/ObjectOrientationTutorial
User avatar
Hexenhammer
Party member
Posts: 175
Joined: Sun Feb 17, 2013 8:19 am

Re: Is there a point to getters and setters in Lua?

Post by Hexenhammer »

There are quite a few reasons to use them.

* Handling side effects

E.g.

Code: Select all

Character.Damage = function(self, damage)

  self.damage = self.damage + damage

  if self.damage > self.health then self:Die() end 

  Game.charactersDamagedThisTurn:Add(self)

  etc..

end
* Implementing attributes whose effective value is context dependent.

Code: Select all

Character.GetStrength = function(self)

  if TerrainAt(self.position) == self.favoredTerrain then 

       return self.strength + Bonus.FavoredTerrain

   end

  return self.strength

end
* You cannot compose regular assignment. You can compose setters.

Code: Select all

return Character():Name("Frank"):Surname("Lee")
* To prevent the assignment of invalid values

Code: Select all

Character.Level = function(self, level)

  if level < 1 or level > 99 then Abort("Character level range is limited to 1-99") end

  self.level = level

end
* Because data structures are more likely to change than interfaces

.. and probably some other things I didn't spontaneously think of. Many things really.
Post Reply

Who is online

Users browsing this forum: No registered users and 70 guests