New object system in 7 LoC

classic Classic list List threaded Threaded
18 messages Options
Reply | Threaded
Open this post in threaded view
|

New object system in 7 LoC

Stefan Reich
Hi guys!

I just created a neat little object system for Lua (5.1) that does pretty much exactly what I want.

You use it like this:

newThing = object(function() -- could add arguments here too
  var1 = 1
  local var2 = 'test'

  function method1()
    var1 = var1+1
  end

  local function method2()
    return string.sub(var2, 2)   -- can use globals as normal
  end
end)

thing = newThing()
thing.var1 = 5
thing.method1()   -- yeah, just dot, no colon

The point is: You just put all fields and methods inside one big definition function. What you declare 'local' will be private; everything else will become part of the object.

The great advantage: No need to ever write 'self.'. (I'm coming from the Java world and I just hate having to insert 'self' all the time (or at all).)

Also, all non-local variables are both readable and writeable from outside.

Usually I write a module 'plain' first (everything top-level) and decide to make it an object later. With this system, I can do that without changing one bit in the code.

There is one little caveat: The definition function must not be recursive (making more objects of its own kind); that would probably break the setfenv trickery. Making more objects within methods is fine.

Here's how it is implemented. Attention, it's short - 14 lines with comments and whitespace :)

*****
-- make an object from a definition function.
-- all variables and functions declared in the definition function without 'local'
-- are automatically part of the new object.
function makeObject(definition, ...)
  -- make an environment that inherits all the globals
  local env = setmetatable({}, {__index = function(_, k) return _G[k] end})   
  setfenv(definition, env)(...)
  return env
end

-- the beloved curry operator (for one fixed arg)
function curry(f, a) return function(...) return f(a, ...) end end

function object(definition) return curry(makeObject, definition) end
*****

You can also put it all in one function, but it still ends up taking 7 LoC:

***
function object(def)
  return function(...)
    local env = setmetatable({}, {__index = function(_, k) return _G[k] end})   
    setfenv(def, env)(...)
    return env
  end
end
***

Is this old? Is this new? Has this been done before? I love this and I think I'll be using it to death. :)

Cheers,
Stefan
Reply | Threaded
Open this post in threaded view
|

Re: New object system in 7 LoC

文曦畅
great trick

honestly, i prefer the way that javascript does.

but the lua way is not bad :-)
easy to keep c-api and lua-api in the same style.


于 2011-11-20 20:27, Stefan Reich 写道:

> Hi guys!
>
> I just created a neat little object system for Lua (5.1) that does
> pretty much exactly what I want.
>
> You use it like this:
>
> newThing = object(function() -- could add arguments here too
>   var1 = 1
>   local var2 = 'test'
>
>   function method1()
>     var1 = var1+1
>   end
>
>   local function method2()
>     return string.sub(var2, 2)   -- can use globals as normal
>   end
> end)
>
> thing = newThing()
> thing.var1 = 5
> thing.method1()   -- yeah, just dot, no colon
>
> The point is: You just put all fields and methods inside one big
> definition function. What you declare 'local' will be private;
> everything else will become part of the object.
>
> The great advantage: No need to ever write 'self.'. (I'm coming from
> the Java world and I just hate having to insert 'self' all the time
> (or at all).)
>
> Also, all non-local variables are both readable and writeable from
> outside.
>
> Usually I write a module 'plain' first (everything top-level) and
> decide to make it an object later. With this system, I can do that
> without changing one bit in the code.
>
> There is one little caveat: The definition function must not be
> recursive (making more objects of its own kind); that would probably
> break the setfenv trickery. Making more objects within methods is fine.
>
> Here's how it is implemented. Attention, it's short - 14 lines with
> comments and whitespace :)
>
> *****
> -- make an object from a definition function.
> -- all variables and functions declared in the definition function
> without 'local'
> -- are automatically part of the new object.
> function makeObject(definition, ...)
>   -- make an environment that inherits all the globals
>   local env = setmetatable({}, {__index = function(_, k) return _G[k]
> end})
>   setfenv(definition, env)(...)
>   return env
> end
>
> -- the beloved curry operator (for one fixed arg)
> function curry(f, a) return function(...) return f(a, ...) end end
>
> function object(definition) return curry(makeObject, definition) end
> *****
>
> You can also put it all in one function, but it still ends up taking 7
> LoC:
>
> ***
> function object(def)
>   return function(...)
>     local env = setmetatable({}, {__index = function(_, k) return
> _G[k] end})
>     setfenv(def, env)(...)
>     return env
>   end
> end
> ***
>
> Is this old? Is this new? Has this been done before? I love this and I
> think I'll be using it to death. :)
>
> Cheers,
> Stefan



Reply | Threaded
Open this post in threaded view
|

Re: New object system in 7 LoC

joao lobato
In reply to this post by Stefan Reich
On 11/20/11, Stefan Reich <[hidden email]> wrote:
> Hi guys!
>
> I just created a neat little object system for Lua (5.1) that does pretty
> much exactly what I want.

Have you considered not having an object system?

Reply | Threaded
Open this post in threaded view
|

Re: New object system in 7 LoC

Petite Abeille

On Nov 20, 2011, at 5:49 PM, joao lobato wrote:

>> I just created a neat little object system for Lua (5.1) that does pretty
>> much exactly what I want.
>
> Have you considered not having an object system?

Ouch! That would require a, hmmm, higher level of consciousness. Might take a tiny bit longer to get there :))


Reply | Threaded
Open this post in threaded view
|

Re: New object system in 7 LoC

Michal Kottman
In reply to this post by Stefan Reich
Just a little note, this:

>   local env = setmetatable({}, {__index = function(_, k) return _G[k] end})

Is equivalent to this:

>   local env = setmetatable({}, {__index = _G})

The second approach is a bit faster (warning: a very simplistic
micro-benchmark follows):

local N = 10000000
function runBench(name, env)
    local start = os.clock()
    for i=1,N do local x = env.print end
    local time = os.clock() - start
    print(name, time)
end
runBench('__index function', setmetatable({}, {__index = function(_,k)
return _G[k] end}))
runBench('__index table', setmetatable({}, {__index = _G}))

This is on a 2.3GHz Core i5 with Lua 5.1.4:

__index function 1.062802
__index table 0.372607

> Is this old? Is this new? Has this been done before?

The idea of not needing a colon for method call is not new - objects
via closures are discussed in PiL [1], and can be seen in various
projects like Lunatic Python [2].

[1] http://www.lua.org/pil/16.4.html
[2] http://labix.org/lunatic-python

Reply | Threaded
Open this post in threaded view
|

Re: New object system in 7 LoC

Stefan Reich
In reply to this post by Petite Abeille
On Sun, Nov 20, 2011 at 6:17 PM, Petite Abeille <[hidden email]> wrote:

On Nov 20, 2011, at 5:49 PM, joao lobato wrote:

>> I just created a neat little object system for Lua (5.1) that does pretty
>> much exactly what I want.
>
> Have you considered not having an object system?

Ouch! That would require a, hmmm, higher level of consciousness. Might take a tiny bit longer to get there :))

Are you fucking kidding me? That is your reaction? I am beginning to hate this list.

I am going to make my own list. With positive people. And not all that ridiculous negativity in lua-l. It's really enough now. I don't like that anymore.
Reply | Threaded
Open this post in threaded view
|

Re: New object system in 7 LoC

Petite Abeille

On Nov 20, 2011, at 11:34 PM, Stefan Reich wrote:

> Are you fucking kidding me?

Good, good. You seem to have reached level 2 in Kübler-Ross model. You are on your way to salvation :))


Reply | Threaded
Open this post in threaded view
|

Re: New object system in 7 LoC

joao lobato
In reply to this post by Stefan Reich
On 11/20/11, Stefan Reich <[hidden email]> wrote:

> On Sun, Nov 20, 2011 at 6:17 PM, Petite Abeille
> <[hidden email]>wrote:
>
>>
>> On Nov 20, 2011, at 5:49 PM, joao lobato wrote:
>>
>> >> I just created a neat little object system for Lua (5.1) that does
>> pretty
>> >> much exactly what I want.
>> >
>> > Have you considered not having an object system?
>>
>> Ouch! That would require a, hmmm, higher level of consciousness. Might
>> take a tiny bit longer to get there :))
>>
>
> Are you fucking kidding me? That is your reaction? I am beginning to hate
> this list.
>
> I am going to make my own list. With positive people. And not all that
> ridiculous negativity in lua-l. It's really enough now. I don't like that
> anymore.
>

Well, I didn't mean to offend anyone. You are very entreprising, much
more than me, at any rate. But I did mean to stress that there is a
right tool for very every job and I've experienced before the feeling
that too much OOP "cripples the mind". One's function ends up
overfitting the data.

Of course my personal opinions regarding OOP don't belong in this
list. If, with my remark, you felt I was looking for a fight ("puxar
jogo", in my native Portuguese), I appologize.

Reply | Threaded
Open this post in threaded view
|

Re: New object system in 7 LoC

Matthew Wild
In reply to this post by Stefan Reich
On 20 November 2011 22:34, Stefan Reich
<[hidden email]> wrote:

> On Sun, Nov 20, 2011 at 6:17 PM, Petite Abeille <[hidden email]>
> wrote:
>>
>> On Nov 20, 2011, at 5:49 PM, joao lobato wrote:
>>
>> >> I just created a neat little object system for Lua (5.1) that does
>> >> pretty
>> >> much exactly what I want.
>> >
>> > Have you considered not having an object system?
>>
>> Ouch! That would require a, hmmm, higher level of consciousness. Might
>> take a tiny bit longer to get there :))
>
> Are you fucking kidding me? That is your reaction? I am beginning to hate
> this list.

Yikes, calm down. Petite was just being humorous (as usual), it's
nothing personal. Not all of us are full-time fans of the OOP model
(though plenty are). Due to Lua's very nature you'll find a much
larger number of OOP refugees here than on most other language mailing
lists.

If you can't handle someone having a different viewpoint to you, then
yes, it's wise to make a new list for yourself. Otherwise, just let it
pass... there's no need for heated posts with language like this.

Regards,
Matthew

Reply | Threaded
Open this post in threaded view
|

Re: New object system in 7 LoC

Luiz Henrique de Figueiredo
> Yikes, calm down.

Yes, please! Let's everyone take a deep breath. And let's close this thead.

Reply | Threaded
Open this post in threaded view
|

Re: New object system in 7 LoC

Leo Razoumov
In reply to this post by Stefan Reich
On Sun, Nov 20, 2011 at 07:27, Stefan Reich
<[hidden email]> wrote:

> Hi guys!
>
> I just created a neat little object system for Lua (5.1) that does pretty
> much exactly what I want.
>
> You use it like this:
>
> newThing = object(function() -- could add arguments here too
>   var1 = 1
>   local var2 = 'test'
>
>   function method1()
>     var1 = var1+1
>   end
> [[snip]]

An elegant idea, indeed! And  'self' reference is avoided.

There is one problem, though.
Every time you create a new object instance brand new closures for all
your instance methods are created as well. In case you have many small
objects it can cost you memory and also CPU.

--Leo--

Reply | Threaded
Open this post in threaded view
|

Re: New object system in 7 LoC

Stefan Reich
On Mon, Nov 21, 2011 at 2:15 PM, Leo Razoumov <[hidden email]> wrote:
An elegant idea, indeed! And  'self' reference is avoided.

thx :)

There is one problem, though.
Every time you create a new object instance brand new closures for all
your instance methods are created as well. In case you have many small
objects it can cost you memory and also CPU.

Yeah, that is true. The system is mainly meant for large objects of which relatively few are created at runtime.

In my case, it's for the whole Mobile Lua engine core. Which has a lot of code and few instances (usually just one, but we want to have the option of creating more if needed).

Cheers,
Stefan
Reply | Threaded
Open this post in threaded view
|

Re: New object system in 7 LoC

Stefan Reich
btw: Thanks to the person who suggested the {__index = _G} optimisation which is a very good one, naturally.

(I don't remember who made the suggestion - and I don't want to wade through the negative responses in the thread anymore, so I thought I'd give back some kudos like this.)
Reply | Threaded
Open this post in threaded view
|

Re: New object system in 7 LoC

Bertrand Mansion
In reply to this post by Petite Abeille
On Sun, Nov 20, 2011 at 11:45 PM, Petite Abeille
<[hidden email]> wrote:
>
> On Nov 20, 2011, at 11:34 PM, Stefan Reich wrote:
>
>> Are you fucking kidding me?
>
> Good, good. You seem to have reached level 2 in Kübler-Ross model. You are on your way to salvation :))

LMAO, I didn't know this one :)

@stefan Seriously, it's not the first time you get upset when people
express what they think of your stuff on the list. If you can't get
over it, just keep it for yourself or don't ask for comments, or don't
read the comments. And even if you get upset (and I don't see any
reason to be upset at this stage), please stop using rude words, it
hurts my feelings.
Thanks,

--
Bertrand Mansion

Reply | Threaded
Open this post in threaded view
|

Re: New object system in 7 LoC

Bernd Eggink
In reply to this post by Leo Razoumov
On 21.11.2011 15:15, Leo Razoumov wrote:

> On Sun, Nov 20, 2011 at 07:27, Stefan Reich
> <[hidden email]>  wrote:
>> Hi guys!
>>
>> I just created a neat little object system for Lua (5.1) that does pretty
>> much exactly what I want.
>>
>> You use it like this:
>>
>> newThing = object(function() -- could add arguments here too
>>    var1 = 1
>>    local var2 = 'test'
>>
>>    function method1()
>>      var1 = var1+1
>>    end
>> [[snip]]
>
> An elegant idea, indeed! And  'self' reference is avoided.

It may look elegant, but it has some severe flaws (IMHO).

1. If the function's environment is made identical to the global
environment, any global variable X can be accessed as obj.X. This is at
least confusing and contradicts the OO paradigma. Example:

--------------------------------------------------------
y = 222

function Class(c)
     x = c
end

function ctor(definition)
     return function(...)
         local env = setmetatable({}, {__index = _G })
         setfenv(definition, env)(...)
         return env
     end
end

obj = ctor(Class)(1)
print(obj.y)   --> 222
--------------------------------------------------------

2. In the example above, obj.x can be read and changed from outside. To
prevent this, one would normally make the variable local. This leads to
even more weirdness:

--------------------------------------------------------
x = 111

function Class(c)
     local x = c
     function pr() print("obj.x", x) end
end

function ctor(definition)
     return function(...)
         local env = setmetatable({}, {__index = _G })
         setfenv(definition, env)(...)
         return env
     end
end

obj = ctor(Class)(1)

print(obj.x, x)   --> 111 111
obj.pr()          --> 1

obj.x=112
print(obj.x, x)   --> 112 111
obj.pr()          --> 1
--------------------------------------------------------

I admit that I don't quite understand why the last example behaves as it
does. I would be glad if somebody could explain what happens here; I'm
experienced in Java, but still a beginner in Lua.

Regards,
Bernd

--
http://sudrala.de

Reply | Threaded
Open this post in threaded view
|

Re: New object system in 7 LoC

Stefan Reich
In reply to this post by Bertrand Mansion


On Mon, Nov 21, 2011 at 6:05 PM, Bertrand Mansion <[hidden email]> wrote:
On Sun, Nov 20, 2011 at 11:45 PM, Petite Abeille
<[hidden email]> wrote:
>
> On Nov 20, 2011, at 11:34 PM, Stefan Reich wrote:
>
>> Are you fucking kidding me?
>
> Good, good. You seem to have reached level 2 in Kübler-Ross model. You are on your way to salvation :))

LMAO, I didn't know this one :)

@stefan Seriously, it's not the first time you get upset when people
express what they think of your stuff on the list. If you can't get
over it, just keep it for yourself or don't ask for comments, or don't
read the comments. And even if you get upset (and I don't see any
reason to be upset at this stage), please stop using rude words, it
hurts my feelings.
Thanks,

Please stop shitting on this list. Thanks. Go away.

Someone who supposes that I would need some "higher consciousness" is clearly lying. I have the highest consciousness that you can have on earth. There is nothing higher than that. So the person is a liar.

Why do you people defend liars? What the fuck? That is totally wrong and I'm not gonna let you do it anymore :-)

Not on my list. And soon all the lists that matter will be "my list".

Cheers.
Stefan

--
ertrand Mansion


Reply | Threaded
Open this post in threaded view
|

Re: New object system in 7 LoC

Stefan Reich
In reply to this post by Bernd Eggink
Hi Bernd!

Wow, finally!!! A constructive response that actually has THOUGHT in it. I almost thought this list was unable of that.

IMO you are expressing your criticism a little harshly ("severe flaws" that are not really that severe in the end :)), but at least there is technical thought in it and I sincerely applaud that.

I will look into the technical details of your answer very soon and give you an appropriate reply (retort? whatever :)).

Cheers,
Stefan

On Sat, Nov 26, 2011 at 7:58 PM, Bernd Eggink <[hidden email]> wrote:
On 21.11.2011 15:15, Leo Razoumov wrote:
On Sun, Nov 20, 2011 at 07:27, Stefan Reich
<[hidden email]>  wrote:
Hi guys!

I just created a neat little object system for Lua (5.1) that does pretty
much exactly what I want.

You use it like this:

newThing = object(function() -- could add arguments here too
  var1 = 1
  local var2 = 'test'

  function method1()
    var1 = var1+1
  end
[[snip]]

An elegant idea, indeed! And  'self' reference is avoided.

It may look elegant, but it has some severe flaws (IMHO).

1. If the function's environment is made identical to the global environment, any global variable X can be accessed as obj.X. This is at least confusing and contradicts the OO paradigma. Example:

--------------------------------------------------------
y = 222

function Class(c)
   x = c
end

function ctor(definition)
   return function(...)
       local env = setmetatable({}, {__index = _G })
       setfenv(definition, env)(...)
       return env
   end
end

obj = ctor(Class)(1)
print(obj.y)   --> 222
--------------------------------------------------------

2. In the example above, obj.x can be read and changed from outside. To prevent this, one would normally make the variable local. This leads to even more weirdness:

--------------------------------------------------------
x = 111

function Class(c)
   local x = c
   function pr() print("obj.x", x) end
end

function ctor(definition)
   return function(...)
       local env = setmetatable({}, {__index = _G })
       setfenv(definition, env)(...)
       return env
   end
end

obj = ctor(Class)(1)

print(obj.x, x)   --> 111 111
obj.pr()          --> 1

obj.x=112
print(obj.x, x)   --> 112 111
obj.pr()          --> 1
--------------------------------------------------------

I admit that I don't quite understand why the last example behaves as it does. I would be glad if somebody could explain what happens here; I'm experienced in Java, but still a beginner in Lua.

Regards,
Bernd

--
http://sudrala.de


Reply | Threaded
Open this post in threaded view
|

Re: New object system in 7 LoC

Gaspard Bucher
[warning: meta post, no irony]

Hi Stefan (and PetiteAbeille and others),

I really enjoy all the fights you are starting around here: it gives me a lot of brain food, mainly on two topics:

I. The poorness of email communication. This is a fact we all know and tend to forget, but emails on a mailing list is probably the poorest means of communication ever designed. We miss all the information transported by body language (attitude, gestures, eye sight, hands), voice (tone, timbre, loudness, etc) and timing (is it a fast response?, a well thought argument?, etc). But this is not all, the inherent segmentation and slowness of the medium tends to fragment the context into something that quickly becomes unintelligible. Things that can happen in normal conversations if nobody really cares (loosing the thread) happens faster on a mailing list unless careful people keep discussions on topic.

II. The difficulty to gain a place in a community; moreover in a long lasting, fragmented and distributed community with people from many different backgrounds, cultures and expectations. In this context, it is not possible to "cling" to a friend (already a member of said community) for a while in order to be accepted: you have to struggle on your own. And in a technically oriented list like this one, this means that you are judged on merit (constructive replies, technical knowledge, humour, etc) and some things interest people more then other things so the technical merit is really very subjective and slippery. In this regard forums like OSQA (stackoverflow, etc) are better suited at evaluating merit, but I am not sure they can are better tools at community building. Meritocracy is not exactly the coolest place around.

Now, I would like to say that you have pretty well accomplished joining the circle of people on this list that are part of my representation of the community:

- I know your name
- I know some of your projects
- I respect your powerful energy (and language)

In two words, despite all the noise of the big internet, you managed to raise my attention.

I really hope you keep up with this fantastic energy of yours and use the fights to strengthen your muscles and not get beaten !

Cheers and happy dancing !

Gaspard no sorry for feeding the trolls, elves and all the weird creatures of the otherworld.


On Sat, Nov 26, 2011 at 11:45 PM, Stefan Reich <[hidden email]> wrote:
Hi Bernd!

Wow, finally!!! A constructive response that actually has THOUGHT in it. I almost thought this list was unable of that.

IMO you are expressing your criticism a little harshly ("severe flaws" that are not really that severe in the end :)), but at least there is technical thought in it and I sincerely applaud that.

I will look into the technical details of your answer very soon and give you an appropriate reply (retort? whatever :)).

Cheers,
Stefan


On Sat, Nov 26, 2011 at 7:58 PM, Bernd Eggink <[hidden email]> wrote:
On 21.11.2011 15:15, Leo Razoumov wrote:
On Sun, Nov 20, 2011 at 07:27, Stefan Reich
<[hidden email]>  wrote:
Hi guys!

I just created a neat little object system for Lua (5.1) that does pretty
much exactly what I want.

You use it like this:

newThing = object(function() -- could add arguments here too
  var1 = 1
  local var2 = 'test'

  function method1()
    var1 = var1+1
  end
[[snip]]

An elegant idea, indeed! And  'self' reference is avoided.

It may look elegant, but it has some severe flaws (IMHO).

1. If the function's environment is made identical to the global environment, any global variable X can be accessed as obj.X. This is at least confusing and contradicts the OO paradigma. Example:

--------------------------------------------------------
y = 222

function Class(c)
   x = c
end

function ctor(definition)
   return function(...)
       local env = setmetatable({}, {__index = _G })
       setfenv(definition, env)(...)
       return env
   end
end

obj = ctor(Class)(1)
print(obj.y)   --> 222
--------------------------------------------------------

2. In the example above, obj.x can be read and changed from outside. To prevent this, one would normally make the variable local. This leads to even more weirdness:

--------------------------------------------------------
x = 111

function Class(c)
   local x = c
   function pr() print("obj.x", x) end
end

function ctor(definition)
   return function(...)
       local env = setmetatable({}, {__index = _G })
       setfenv(definition, env)(...)
       return env
   end
end

obj = ctor(Class)(1)

print(obj.x, x)   --> 111 111
obj.pr()          --> 1

obj.x=112
print(obj.x, x)   --> 112 111
obj.pr()          --> 1
--------------------------------------------------------

I admit that I don't quite understand why the last example behaves as it does. I would be glad if somebody could explain what happens here; I'm experienced in Java, but still a beginner in Lua.

Regards,
Bernd

--
http://sudrala.de





--

                                                               Gaspard