More on environments and objects

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

More on environments and objects

Chris Marrin

I have been thinking more about the issue of separate environments and how that helps with an object-oriented approach to Lua, with C++ object interactions. As Rici has pointed out, when you have a function representing an object method you really want its environment to be something common to all instances of this object type. Otherwise you will be creating functions for every instance, which would not only bloat memory some, but would take time as well.

If you did have an environment common to all instances of an object type, then it could have an __setindex metamethod. Let's say we have a class called MyClass with an __call metamethod to do instantiation. Further, let's say this class has a member variable 'a' and a member function 'f' defined like this:

    Function MyClass:f(v) a = v end

then you could go:

    local instance = MyClass()
    instance:f(5)

and when f(v) runs, the __setindex metamethod would get called with the parameters of the common function environment, the property 'a' and the value 'v'. This __setindex method can then go up to the next function to the stack and find the 'self' reference as the first param. It can then look up 'a' in this reference, set its value to 'v' and you're done. A nice object-oriented mechanism for handling instance local access.

The problem with this is that you don't really KNOW if the first parameter to the function is the 'self' reference. It may have been called using the SELF opcode to setup the stack. But that fact doesn't leave behind any clues. This is because this:

    instance:f(5)

is equivalent to:

    instance.f(instance, 5)

in Lua. That is really too bad. It can lead to very tough to debug errors. I know that, even though I have been writing Lua for several months now I still often use '.' when I intended to use ':'. Often this is easy to catch: the param after 'self' is something that looks nothing like a self pointer, so an error is generated. But what if you are passing an object that looks like a self pointer as the first param? You might have a function like this:

    function MyClass:g(v)
        if v ~= nil then
            self.a = v.a
        else
            self.a = 10
        end
    end

If you call this like this:

    local instance2 = MyClass()
    instance2.a = 5
    instance:g(instance2)

it works fine. After the call instance.a is 5. But if you do this instead:

    instance.g(instance2)

then you change instance2.a to 10, which was not the intent. No error is issued and it might be very difficult to track this down.

It would not be hard to add something to CallInfo to indicate that this method was called with the SELF instruction. That would no longer allow this:

    instance.g(instance, instance2)

in place of this:

    instance:g(instance2)

but it would still be compatible because I'm making the ':' syntax a requirement in my object system. The rest of Lua works as it always has.

Anyway, does this seem like a reasonable approach?

--
chris marrin              ,""$, "As a general rule,don't solve puzzles
[hidden email]        b`    $  that open portals to Hell" ,,.
        ,.`           ,b`    ,`                            , 1$'
     ,|`             mP    ,`                              :$$'     ,mm
   ,b"              b"   ,`            ,mm      m$$    ,m         ,`P$$
  m$`             ,b`  .` ,mm        ,'|$P   ,|"1$`  ,b$P       ,`  :$1
 b$`             ,$: :,`` |$$      ,`   $$` ,|` ,$$,,`"$$     .`    :$|
b$|            _m$`,:`    :$1   ,`     ,$Pm|`    `    :$$,..;"'     |$:
P$b,      _;b$$b$1"       |$$ ,`      ,$$"             ``'          $$
 ```"```'"    `"`         `""`        ""`                          ,P`

Reply | Threaded
Open this post in threaded view
|

Re: More on environments and objects

Rici Lake-2

On 26-Aug-05, at 9:35 AM, Chris Marrin wrote:

It would not be hard to add something to CallInfo to indicate that this method was called with the SELF instruction. That would no longer allow this:

    instance.g(instance, instance2)

in place of this:

    instance:g(instance2)

but it would still be compatible because I'm making the ':' syntax a requirement in my object system. The rest of Lua works as it always has.

Anyway, does this seem like a reasonable approach?

The problem with this is that sometimes you need to call an instance method supplying the actual instance. This shows up at odd moments; the simplest example I can think of is the following:

table.foreach calls a function for every k,v in a table. It could be written:

function table.foreach(tab, func)
  for k, v in pairs(tab) do
    local rv = func(k, v)
    if rv then return rv end
  end
end

Now, suppose I actually want to invoke an object method, instead of calling a function? I could do that with an explicit wrapper:

  table.foreach(tab, function(k, v) return obj:method(k, v) end)

but that would get tedious, so I would want to refactor it:

function table.foreachmeth(obj, tab, method)
  for k, v in pairs(tab) do
    local rv = obj[method](obj, k, v)
    if rv then return rv end
  end
end

table.foreachmeth(obj, t, "send")

I could even introduce that into the object:

obj.foreach = foreachmeth
obj:foreach(tab, "send")

How would I write this if the method refused to execute unless self-called? The best I can come up with is filling the method name into a template and then compiling the template, which seems yukky.



Reply | Threaded
Open this post in threaded view
|

Re: More on environments and objects

Rici Lake-2
In reply to this post by Chris Marrin

On 26-Aug-05, at 9:35 AM, Chris Marrin wrote:

    Function MyClass:f(v) a = v end

then you could go:

    local instance = MyClass()
    instance:f(5)

and when f(v) runs, the __setindex metamethod would get called with the parameters of the common function environment, the property 'a' and the value 'v'. This __setindex method can then go up to the next function to the stack and find the 'self' reference as the first param. It can then look up 'a' in this reference, set its value to 'v' and you're done. A nice object-oriented mechanism for handling instance local access.

Isn't this a complicated way of confusing the difference between instance and class members? I would have thought that:

  function MyClass:f(v) self.a = v end

was not much harder to type and quite a bit more self-documenting.

I'm aware that some languages do perform this sort of scoping exercise, so I guess some people must like it. I think it also leads to obscure bugs.

That doesn't avoid the self confusion error which you noted.

However, in many cases you can avoid the problem by using get/set notation instead of methods. If the user interface were:

  obj.f = v

instead of

  obj:f(v)

then there would be no possibility of programmer error, and it would have the advantage of letting you define both an f-setter and an f-getter. (Or the disadvantage of forcing you to :) Anyway, that was the motivation for the library for which I sent you an excerpt.

This doesn't work in the case of setters which require more than one argument, or getters which require arguments. But that's not the common case (although it's not a rare case, either).


Reply | Threaded
Open this post in threaded view
|

Re: More on environments and objects

Chris Marrin
In reply to this post by Rici Lake-2
Rici Lake wrote:
...
Now, suppose I actually want to invoke an object method, instead of calling a function? I could do that with an explicit wrapper:

  table.foreach(tab, function(k, v) return obj:method(k, v) end)

but that would get tedious, so I would want to refactor it:

function table.foreachmeth(obj, tab, method)
  for k, v in pairs(tab) do
    local rv = obj[method](obj, k, v)
    if rv then return rv end
  end
end

Right. But if Lua understood that obj[method] was a member function, it could push it implicitly always. And that would get rid of the ':' vs '.' issue as well. You always use '.' and the runtime decides which functions are object methods and which are static. This might require some additional syntax at the point of function declaration, but that should not be too hard.

Anyway, never satisfied :-)

--
chris marrin                ,""$,
[hidden email]          b`    $                             ,,.
                        mP     b'                            , 1$'
        ,.`           ,b`    ,`                              :$$'
     ,|`             mP    ,`                                       ,mm
   ,b"              b"   ,`            ,mm      m$$    ,m         ,`P$$
  m$`             ,b`  .` ,mm        ,'|$P   ,|"1$`  ,b$P       ,`  :$1
 b$`             ,$: :,`` |$$      ,`   $$` ,|` ,$$,,`"$$     .`    :$|
b$|            _m$`,:`    :$1   ,`     ,$Pm|`    `    :$$,..;"'     |$:
P$b,      _;b$$b$1"       |$$ ,`      ,$$"             ``'          $$
 ```"```'"    `"`         `""`        ""`                          ,P`
"As a general rule,don't solve puzzles that open portals to Hell"'

Reply | Threaded
Open this post in threaded view
|

Re: More on environments and objects

Chris Marrin
In reply to this post by Rici Lake-2
Rici Lake wrote:

On 26-Aug-05, at 9:35 AM, Chris Marrin wrote:

    Function MyClass:f(v) a = v end

then you could go:

    local instance = MyClass()
    instance:f(5)

and when f(v) runs, the __setindex metamethod would get called with the parameters of the common function environment, the property 'a' and the value 'v'. This __setindex method can then go up to the next function to the stack and find the 'self' reference as the first param. It can then look up 'a' in this reference, set its value to 'v' and you're done. A nice object-oriented mechanism for handling instance local access.


Isn't this a complicated way of confusing the difference between instance and class members? I would have thought that:

  function MyClass:f(v) self.a = v end

was not much harder to type and quite a bit more self-documenting.

I'm aware that some languages do perform this sort of scoping exercise, so I guess some people must like it. I think it also leads to obscure bugs.

That doesn't avoid the self confusion error which you noted.

However, in many cases you can avoid the problem by using get/set notation instead of methods. If the user interface were:

  obj.f = v

instead of

  obj:f(v)

then there would be no possibility of programmer error, and it would have the advantage of letting you define both an f-setter and an f-getter. (Or the disadvantage of forcing you to :) Anyway, that was the motivation for the library for which I sent you an excerpt.

This doesn't work in the case of setters which require more than one argument, or getters which require arguments. But that's not the common case (although it's not a rare case, either).

All good points. It is becoming clear that, while Lua is well suited to doing just about anything (just like assembly language :-), it will never be a true object-oriented language. There will always be ':' vs. '.' issues, places where the language just can't express an object-oriented relationship (as in the case where you just HAVE to supply an explicit 'self' argument) and the fairly quirky way that some of the metamethods work.

Don't get me wrong, I think Lua is a fantastic tool and I would never consider another language for my app. But I am really trying to do two things with Lua. First, I am using it as a basic scripting technique to get my app on the air faster. But my system is declarative and therefore I need to expose scripting to creative authors who are not experienced programmers. So I need to make the language as simple and understandable as possible.

Just to harp on another issue, I was very proud when I overrode all the arithmetic opcodes to be able to handly my "primitive objects". My system has objects, for instance SFFloat, which are wrappers around primitives (float in this case). If I have:

    local a = SFFloat(1)

I really want to go:

    local b = a + 5

So I overrode __add and simply converted the object to a primitive, did the add and returned the result. Cool! Well, when I overrode __lt so I could do this:

    if a < 5 then ...

it didn't work because the logic around __lt requires that both sides of the operator are the same type! I mentioned this on the list before and had to make a small patch to get this to work. But then I wanted to do this:

    local c = SFBoolean(true)
    if c then ...

and that didn't work at all. SFBoolean is a wrapper around a bool, but Lua sees it as a userdata and the l_isfalse() test simply checks if it is a Lua boolean value, so this always tests as false. It would be nice if there were __toboolean and __tonumber metamethods, just like there is __tostring.

I have had to resort to a valueOf() method on these objects. So you have to go:

    if c:valueOf() then ...

not bad, but not very friendly to the authors either!

Anyway, thanks for engaging on this issue.

--
chris marrin                ,""$,
[hidden email]          b`    $                             ,,.
                        mP     b'                            , 1$'
        ,.`           ,b`    ,`                              :$$'
     ,|`             mP    ,`                                       ,mm
   ,b"              b"   ,`            ,mm      m$$    ,m         ,`P$$
  m$`             ,b`  .` ,mm        ,'|$P   ,|"1$`  ,b$P       ,`  :$1
 b$`             ,$: :,`` |$$      ,`   $$` ,|` ,$$,,`"$$     .`    :$|
b$|            _m$`,:`    :$1   ,`     ,$Pm|`    `    :$$,..;"'     |$:
P$b,      _;b$$b$1"       |$$ ,`      ,$$"             ``'          $$
 ```"```'"    `"`         `""`        ""`                          ,P`
"As a general rule,don't solve puzzles that open portals to Hell"'

Reply | Threaded
Open this post in threaded view
|

Re: More on environments and objects

Rici Lake-2

On 26-Aug-05, at 7:09 PM, Chris Marrin wrote:

It is becoming clear that, while Lua is well suited to doing just about anything (just like assembly language :-), it will never be a true object-oriented language. There will always be ':' vs. '.' issues, places where the language just can't express an object-oriented relationship (as in the case where you just HAVE to supply an explicit 'self' argument) and the fairly quirky way that some of the metamethods work.

All reasonable languages are Turing-complete :)

We could get into a long philosophical argument here: "object-oriented" does not mean "class-oriented", imho, and a prototype language like newtonscript, self or lua (to name a few) is every bit as object oriented as java or c++. (Speaking of which, I don't see much difference between the Lua example I wrote and a similar one using c++'s pointer-to-member-function syntax. Maybe that's just me.)


Don't get me wrong, I think Lua is a fantastic tool and I would never consider another language for my app. But I am really trying to do two things with Lua. First, I am using it as a basic scripting technique to get my app on the air faster. But my system is declarative and therefore I need to expose scripting to creative authors who are not experienced programmers. So I need to make the language as simple and understandable as possible.

Sure, I understand that. I used to provide objects that didn't use the : syntax at all, despite the general inefficiency. I stopped doing it because it gets to be a pain. But here's the quick outline. First you need to separate out your methods from any non-method class members; the simplest way to do that is to keep them in a separate table, and make that table the __index for the class table's __index table. This works fine if you only allow member functions to inherit; then the inheritance __index links are between method tables rather than class tables.

But instead of using a simple __index => table entry in the class table, you use some variation on this function, which I've spelled out a bit

function bind(self, methodname)
  local methodtable = self.__method
  local method = methodtable[methodname]
  if method then
    local function boundmethod(...)
      return method(self, unpack(arg))
    end
-- if they call a method once, they may call it again. So we cache it.
    self[methodname] = boundmethod
    return boundmethod
  end
end

Line 3 will pick up the inherited method from the methodtable inheritance chain. The currying procedure in lines 5-6 is ugly, slow and unreliable; it's much easier to write in C. However, that introduces a C call into the call chain, which is also ugly (it stops yield from working, for example.) There are a number of alternatives, the most interesting of which is to figure out how many parameters the method takes (which is a pretty simple modification to the debug interface) and then use a dynamically generated and memoised curry function factory. Fortunately, Lua 5.1 provides a much easier and efficient feature: just replace line 6 with

   return method(self, ...)

This mechanism is not that different from Python, and like Python it suffers from unpredictability (it really is predictable but you have to read and understand five pages of fine print to be able to predict it) and excessive malloc'ing.

Anyway, I gave up and made friends with colon, and I'm much happier for it. (One of my more common C errors is using . instead of -> and vice versa. It's not a problem unique to Lua.)

Just to harp on another issue, I was very proud when I overrode all the arithmetic opcodes to be able to handly my "primitive objects". My system has objects, for instance SFFloat, which are wrappers around primitives (float in this case). If I have:

    local a = SFFloat(1)

I really want to go:

    local b = a + 5

So I overrode __add and simply converted the object to a primitive, did the add and returned the result.

I can't help thinking that it would be easier to convert the SFFloats to numbers when you introduce them into the Lua environment, and back to SFFloats when you export them. Again, this could easilly be done with getter/setter methods.

 Cool! Well, when I overrode __lt so I could do this:

    if a < 5 then ...

it didn't work because the logic around __lt requires that both sides of the operator are the same type!

Actually, it only requires them to have the same metamethod. In Lua 5.1, primitive types have metatables, so you could define the __lt metamethod for Lua numbers to be the same metamethod as your Numeric types. This wouldn't slow down Lua's actual numeric comparisons since the Number metatable isn't consulted. Having said that, I haven't checked the code to see whether that will work -- it may be that Lua still insists that comparable objects have the same Lua type -- but the patch would be a lot cleaner.

    local c = SFBoolean(true)
    if c then ...

and that didn't work at all.

Well, it would, but SFBoolean(false) wouldn't. But why do you need to export SFBoolean(true) into the Lua environment? Surely it is easy enough to convert true <-> SFTrue and false <-> SFFalse. (In any event, in 5.1 you can give boolean a metatable.)

It would be nice if there were __toboolean and __tonumber metamethods, just like there is __tostring.

This wouldn't really work in the case of __tonumber; you would have to restrict it to the tonumber() explicit coercion.

Suppose you had   a + b

where a and b are objects. Does this mean

  meta(a):__tonumber(a) + meta(b):__tonumber(b)

or

  meta(a):__add(b)

(Consider the case of a Complex object type to put that into perspective.)

When false was introduced into the language, I observed that when you have exactly two of anything, you've probably got a design error. (If you have exactly three or exactly 11, it's even worse.) The only quantities that should appear in designs are "one" and "an arbitrary number of".

So having two values which test false fails my test. My suggestion was a __false metafield: if present, it would be expected to be the boolean false, but it could be a function, I suppose. The performance hit of calling a function for every boolean operation is a bit ugly, though.

Anyway, that didn't happen and I'm not really disappointed. (I'd still be happy to go back to only 'nil' testing false, though.) One of the things I really like about Lua is that 0 and "0" and {} are all true. (Particularly "0".) If you mean something different you should say it.

I have had to resort to a valueOf() method on these objects. So you have to go:

    if c:valueOf() then ...

not bad, but not very friendly to the authors either!

Friendly to the authors would be not trying to wrap true and false as pseudo-objects, or using the 5.1 metatables to do so. Failing that, I'd go for:

    if c == SFTrue then ...

or

    if c ~= SFFalse then

or even

    if Boolean(c) then

in preference to c:valueOf(), which doesn't give me much of a hint what it's supposed to do.

But maybe that's just me.

Anyway, good luck.


Reply | Threaded
Open this post in threaded view
|

Re: More on environments and objects

Petite Abeille

On Aug 27, 2005, at 09:27, Rici Lake wrote:

Sure, I understand that. I used to provide objects that didn't use the : syntax at all, despite the general inefficiency. I stopped doing it because it gets to be a pain.

I have to admit that after 6 months of cogitation I have learned to love the colon as well. Smooth sealing since then :)

That said, I still have one fundamental issue:

- How to properly handle invocation of parent's methods?

Quite often, I want to simply extend a method implementation, not overwrite it entirely.

For illustration purpose, lets assume I have a DAVService class to handle WebDAV requests. DAVService extends ObjectService, which in turn extends Service. Each class provides an initialization method which needs to be properly chained. In other words, a subclass needs to invoke its parent implementation. Something along these lines:

-- DAVService initialization
function self:init( aPrefix, aPath, anAuthenticator )
self:invokeSuper( ObjectService, "init", aPrefix, anAuthenticator )

        self._path = aPath

        return self
end

-- ObjectService initialization
function self:init( aPrefix, aType, anAuthenticator )
        self:invokeSuper( Service, "init",  aPrefix, anAuthenticator )

        self._type = aType
        self:services():put( aType, self )

        return self
end

-- Service initialization
function self:init( aPrefix, anAuthenticator )
        self._prefix = aPrefix
        self._authenticator = anAuthenticator

        return self
end

Note the quirky 'invokeSuper' method, which lookups a given class method and invokes it:

function self:invokeSuper( aSuper, aMethod, ... )
        local aFunction = aSuper[ aMethod ]

        return aFunction( self, unpack( arg ) )
end

Note how I need to pass the effective super class to the method. This is the crux of the problem as it requires the entire inheritance chain to be spelled out explicitly instead of simply using something like self:super():doIt().

Haven't cracked this one yet :/

Sigh...

Cheers

--
PA, Onnay Equitursay
http://alt.textdrive.com/


Reply | Threaded
Open this post in threaded view
|

Re: More on environments and objects

Mike Pall-57
Hi,

PA wrote:
> I have to admit that after 6 months of cogitation I have learned to 
> love the colon as well. Smooth sealing since then :)

Interesting. Looks like many programmers (when new to Lua or
to its ancestor languages) experience the same learning curve.

Usually the discussions go along this path:

1. implicit local vs. explicit local.
2. static typing vs. dynamic typing.
3. specialized types vs. limited type diversity.
4. automatic type coercion vs. explicit type coercion.
5. dot-only vs. dot and colon.
6. implicit self vs. explicit self.
7. exception dispatch vs. pcall().
8. object equality vs. object identity.
9. OO vs. closures.
10. complexity vs. simplicity.

Lua is different. The familiar syntax is deceptive.

> Note how I need to pass the effective super class to the method. This 
> is the crux of the problem as it requires the entire inheritance chain 
> to be spelled out explicitly instead of simply using something like 
> self:super():doIt().

5a. implicit super vs. explicit super.


My personal there's-got-to-be-a-better-way list for Lua
is pretty short nowadays (thanks to Lua 5.1):

- Yield from everywhere.
- Explicit finalization.
- Standardized module installation.
- Userdata type checks.
- Better Lua/C API (something for 6.x).

And a few minor issues with out-of-the-box behaviour:

- Hex numbers (parser + luaO_str2d with strtol fallback for non-C99)
- Missing bit operations (math.*)
- xpcall()
- package.cpath (as of 51w6)
- Makefile (OS detection, -ldl or not, creating module dirs etc.)
- lua_Number (support for types other than double)

Bye,
     Mike

Reply | Threaded
Open this post in threaded view
|

Re: More on environments and objects

Chris Marrin
In reply to this post by Rici Lake-2
Rici Lake wrote:

...
Just to harp on another issue, I was very proud when I overrode all the arithmetic opcodes to be able to handly my "primitive objects". My system has objects, for instance SFFloat, which are wrappers around primitives (float in this case). If I have:

    local a = SFFloat(1)

I really want to go:

    local b = a + 5

So I overrode __add and simply converted the object to a primitive, did the add and returned the result.


I can't help thinking that it would be easier to convert the SFFloats to numbers when you introduce them into the Lua environment, and back to SFFloats when you export them. Again, this could easilly be done with getter/setter methods.

The whole point of having objects represent primitives is because they have extra functionality, like firing events which can be routed to properties of other objects. So Lua really needs to know about their object-ness. And the whole point of making this author-friendly is to avoid getter and setter methods.


 Cool! Well, when I overrode __lt so I could do this:

    if a < 5 then ...

it didn't work because the logic around __lt requires that both sides of the operator are the same type!


Actually, it only requires them to have the same metamethod. In Lua 5.1, primitive types have metatables, so you could define the __lt metamethod for Lua numbers to be the same metamethod as your Numeric types. This wouldn't slow down Lua's actual numeric comparisons since the Number metatable isn't consulted. Having said that, I haven't checked the code to see whether that will work -- it may be that Lua still insists that comparable objects have the same Lua type -- but the patch would be a lot cleaner.

Right, I have thought that the new primitive metatables might help me. But I have not fiddled with them yet.

...
It would be nice if there were __toboolean and __tonumber metamethods, just like there is __tostring.


This wouldn't really work in the case of __tonumber; you would have to restrict it to the tonumber() explicit coercion.

Suppose you had   a + b

where a and b are objects. Does this mean

  meta(a):__tonumber(a) + meta(b):__tonumber(b)

or

  meta(a):__add(b)

(Consider the case of a Complex object type to put that into perspective.)

Couldn't you have a rule that first tried the first way and if that did not work (because there is not __tonumber metamethod for that object, for instance) you would try __add? I agree that __add is needed for things like complex numbers. I don't happen to have that issue, though.

...
Anyway, that didn't happen and I'm not really disappointed. (I'd still be happy to go back to only 'nil' testing false, though.) One of the things I really like about Lua is that 0 and "0" and {} are all true. (Particularly "0".) If you mean something different you should say it.

I was pissed off at first that 0 tested true in Lua. I've never seen another mainstream language that does this. But I am not too mad about it. I have no problem forcing my authors to do:

    if a == 0 then ...


I have had to resort to a valueOf() method on these objects. So you have to go:

    if c:valueOf() then ...

not bad, but not very friendly to the authors either!


Friendly to the authors would be not trying to wrap true and false as pseudo-objects, or using the 5.1 metatables to do so. Failing that, I'd go for:

    if c == SFTrue then ...

or

    if c ~= SFFalse then

or even

    if Boolean(c) then

But for all intents and purposes an SFBoolean object IS a boolean primitive. The extra features that SFBoolean provides has nothing to do with its data type, only with how it can interact with other properties in the system. What I really want is for Lua not to judge me for how I want to coerce the object system :-)


in preference to c:valueOf(), which doesn't give me much of a hint what it's supposed to do.

This brings up what seems to be a dirty word here and many other language oriented places, Javascript. I say that because I see people all over the place mention the "top languages" as Python, C++, Java, event Self (which I have taken to be a codeword for Javascript). I'm not sure why that it. Is it because Javascript has been erroneously associated with Java by its poor naming, or is it because people don't like to type ECMAScript, or is it because it has to much of the cachet of that other persona non grata language, Basic? It's almost like saying Voldemort in the Harry Potter books! But it is one of the most popular languages in existance, given the millions of web pages it appears on.

Anyway, many of these concepts come from The-Language-That-Must-Not-Be-Named.

In particular, valueOf() returns a string, number or boolean primitive if the object is naturally of that type. Otherwise it returns the object that was passed. It is used to solve this exact problem. It is hardly ever used in Javascript because it does all these transformations implicitly.

Anyway, my goal is to get as many Javascript features into the Lua-based Object Model as possible. I am even considering writing a Javascript-to-Lua translator so my authors can use that syntax. That will solve the "Lua is just too oddball" complaint I am getting. This solution may not provide as rich a solution as Lua is capable of. But native Lua will still be there when needed so nothing will be lost!

--
chris marrin              ,""$, "As a general rule,don't solve puzzles
[hidden email]        b`    $  that open portals to Hell" ,,.
        ,.`           ,b`    ,`                            , 1$'
     ,|`             mP    ,`                              :$$'     ,mm
   ,b"              b"   ,`            ,mm      m$$    ,m         ,`P$$
  m$`             ,b`  .` ,mm        ,'|$P   ,|"1$`  ,b$P       ,`  :$1
 b$`             ,$: :,`` |$$      ,`   $$` ,|` ,$$,,`"$$     .`    :$|
b$|            _m$`,:`    :$1   ,`     ,$Pm|`    `    :$$,..;"'     |$:
P$b,      _;b$$b$1"       |$$ ,`      ,$$"             ``'          $$
 ```"```'"    `"`         `""`        ""`                          ,P`

Reply | Threaded
Open this post in threaded view
|

Re: More on environments and objects

Rici Lake-2
In reply to this post by Mike Pall-57
Good list.

On 27-Aug-05, at 9:36 AM, Mike Pall wrote:

My personal there's-got-to-be-a-better-way list for Lua
is pretty short nowadays (thanks to Lua 5.1):

- Yield from everywhere.

I'm starting to think that yield is hiding an ambiguity. The fact that you cannot use yield to both implement control-inversion and thread-scheduling independently makes me wonder if there isn't some better way.

- Explicit finalization.

Agreed.

- Standardized module installation.

Agreed.

- Userdata type checks.

I think this can be accomplished easily now that userdata have both "class" and "instance" tables, although I think the mechanism could be improved slightly.

- Better Lua/C API (something for 6.x).

It would have to be even simpler than the current one, which is a challenge.


And a few minor issues with out-of-the-box behaviour:

- Hex numbers (parser + luaO_str2d with strtol fallback for non-C99)

Agreed.

- Missing bit operations (math.*)

I'd put bit operations in string.*, not math.*. We can argue about this after 5.1 comes out.

- xpcall()
- package.cpath (as of 51w6)
- Makefile (OS detection, -ldl or not, creating module dirs etc.)
- lua_Number (support for types other than double)

Agreed with all the above.


Reply | Threaded
Open this post in threaded view
|

Re: More on environments and objects

Aaron Brown-2
In reply to this post by Chris Marrin
Chris Marrin wrote:

> I was pissed off at first that 0 tested true in Lua.
> [...]  But I am not too mad about it.

In my humble opinion, the only languages that have an excuse
for making 0 test false are those that don't have a "nil" or
"false" value (or can't use them in certain circumstances
because of static typing).

-- 
Aaron

Reply | Threaded
Open this post in threaded view
|

Re: More on environments and objects

Aaron Brown-2
In reply to this post by Rici Lake-2
Rici wrote:

> I'd put bit operations in string.*, not math.*.  We can
> argue about this after 5.1 comes out.

Forgive me for jumping the gun, but I'd be interested in
seeing what kind of interface you're thinking of.  Do you
imagine them taking or returning strings?  What kinds?
(Maybe something like string.char(1, 1) represents
0000 0001 0000 0001 i.e., 257?)

-- 
Aaron

Reply | Threaded
Open this post in threaded view
|

Re: More on environments and objects

Rici Lake-2

On 27-Aug-05, at 10:23 AM, Aaron Brown wrote:

Rici wrote:

I'd put bit operations in string.*, not math.*.  We can
argue about this after 5.1 comes out.

Forgive me for jumping the gun, but I'd be interested in
seeing what kind of interface you're thinking of.  Do you
imagine them taking or returning strings?  What kinds?
(Maybe something like string.char(1, 1) represents
0000 0001 0000 0001 i.e., 257?)

Both taking and returning strings.

Numbers have a fixed but unspecified length, so you cannot reliably test bit `i' in a number; you have no way of knowing whether `i' is in range or not.

I use bit operations for fairly long bit strings. Using bits in a number in order to compress a small number of "flags" into a single operation strikes me as an abuse both of flags and numbers, but that might be just me being a purist.

Strings are immutable, so you cannot "set" a bit; you need to create a new bit string with a "bitor" operation. In practice, that is often convenient because the resulting bit strings are interned and can be used as table keys (in some applications, that's exactly what you want; in others, it's a little bit of regrettable overhead).



Reply | Threaded
Open this post in threaded view
|

Re: More on environments and objects

Adrian Sietsma
Rici Lake wrote:


I use bit operations for fairly long bit strings. Using bits in a number in order to compress a small number of "flags" into a single operation strikes me as an abuse both of flags and numbers, but that might be just me being a purist.

my major "number as bitset of flags" use is returning status codes from a function in c. since lua funcs can return mutiple values, and/or an ad-hoc table, it's not a normal usage case.

i would personally use a mutable buffer for the string of digits - give it a string constructor, __tostring() retriever, and have it return 0 for any out-of-range bits. it can auto-expand on bit set if required.
(i'm biased - i have such a buffer class)

Adrian

Reply | Threaded
Open this post in threaded view
|

Re: More on environments and objects

Petite Abeille
In reply to this post by Mike Pall-57

On Aug 27, 2005, at 16:36, Mike Pall wrote:

Interesting. Looks like many programmers (when new to Lua or
to its ancestor languages) experience the same learning curve.

I would guess that the same applies to any/most new languages one tries to learn, no?

8. object equality vs. object identity.

Doesn't the __eq metamethod cater to both?

9. OO vs. closures.

Why would that be a 'versus'? OO provides an organizational principle. Closures, a nifty encapsulation mechanism.

10. complexity vs. simplicity.

Hmmm... yes... well... I still have to see that famed simplicity manifest itself in anything more ambitious than diminutive code snippets. Where is that full fledged, killer Lua application lurking?

5a. implicit super vs. explicit super.

I was kind of hoping that someone would point out a cunning way to have it both ways :))

- Yield from everywhere.

Hurray to that!

Cheers

--
PA, Onnay Equitursay
http://alt.textdrive.com/


Reply | Threaded
Open this post in threaded view
|

Re: More on environments and objects

Adrian Sietsma
In reply to this post by Mike Pall-57
Mike Pall wrote:

Interesting. Looks like many programmers (when new to Lua or
to its ancestor languages) experience the same learning curve.

Usually the discussions go along this path:

1. implicit local vs. explicit local.
2. static typing vs. dynamic typing.
3. specialized types vs. limited type diversity.
4. automatic type coercion vs. explicit type coercion.
5. dot-only vs. dot and colon.
6. implicit self vs. explicit self.
7. exception dispatch vs. pcall().
8. object equality vs. object identity.
9. OO vs. closures.
10. complexity vs. simplicity.

Lua is different. The familiar syntax is deceptive.

true, but many of these (5,7,9) are not either-or.
i have a simple sorted-insert linked-list, implemented as a table of functions with one upvalue. it's in a heat point, and i don't need a metatable, just 3 functions. that said, i love the : syntax for other stuff: setting the metatable of a table to point to table (is that a recursive sentance ?) gives the lovely t:insert() syntax. also complex classes benefit from :.

i have c++ calling lua, and i have lua calling c / c++.

that's what i like most about lua - you can use it in the manner that best suits you and the problem at hand.

Adrian

Reply | Threaded
Open this post in threaded view
|

Re: More on environments and objects

Adrian Sietsma
In reply to this post by Petite Abeille
PA wrote:
Hmmm... yes... well... I still have to see that famed simplicity manifest itself in anything more ambitious than diminutive code snippets. Where is that full fledged, killer Lua application lurking?

why does it need to be a killer app ?
i have hacked up a specialized http proxy server in lua; it's fast enough for the purpose, and it's infinitely faster to modify than the equivalent c/c++ program. it's not a killer app, and will probably be re-implemented as part of a web server, but it was (relatively) quick to develop, it's multiplexed and error-trapped using coroutines.... and when a server dishes up something it can't understand, i can patch it there and then.

Adrian


Reply | Threaded
Open this post in threaded view
|

Re: More on environments and objects

Petite Abeille

On Aug 27, 2005, at 18:56, Adrian Sietsma wrote:

PA wrote:
Hmmm... yes... well... I still have to see that famed simplicity manifest itself in anything more ambitious than diminutive code snippets. Where is that full fledged, killer Lua application lurking?

why does it need to be a killer app ?

Well.. it doesnt... but at least something full fledged. It would not hurt if it was of general interest as well :)

i have hacked up a specialized http proxy server in lua; it's fast enough for the purpose, and it's infinitely faster to modify than the equivalent c/c++ program. it's not a killer app, and will probably be re-implemented as part of a web server, but it was (relatively) quick to develop, it's multiplexed and error-trapped using coroutines.... and when a server dishes up something it can't understand, i can patch it there and then.

Excellent! Is it available publically? Got an URL? Looking forward to see it :)

Cheers

--
PA, Onnay Equitursay
http://alt.textdrive.com/


Reply | Threaded
Open this post in threaded view
|

Bit manipulation functions (was: More on environments and objects)

Mike Pall-57
In reply to this post by Rici Lake-2
Hi,

Rici Lake wrote:
> >- Hex numbers (parser + luaO_str2d with strtol fallback for non-C99)
> 
> Agreed.
> 
> >- Missing bit operations (math.*)
> 
> I'd put bit operations in string.*, not math.*. We can argue about this 
> after 5.1 comes out.

I really want them for manipulating bits in numbers, not for
arbitrary-length bit strings. That's why I mentioned hex
numbers first. Limiting this to int32 is just fine for me
(e.g. for interfacing with C or certain low-level calculations).

[
The underlying type for arbitrary-length bit strings depends
a lot on your usage patterns, but userdata seems to be more
apropriate IMHO. I'd see this more as a small part of an
arbitrary-precision math module (like the GMP binding).
]

But hex numbers and simple bit manipulation could/should be
within the scope of Lua 5.1 IMHO. I already explained how to
do the former. The latter would look something like:

static int math_bitor (lua_State *L) {
  lua_pushinteger(L, luaL_checkinteger(L, 1) | luaL_checkinteger(L, 2));
  return 1;
}

static int math_bitand (lua_State *L) {  /* aka tobit */
  lua_pushinteger(L, luaL_checkinteger(L, 1) & luaL_optinteger(L, 2, -1));
  return 1;
}

static int math_bitxor (lua_State *L) {  /* aka bitnot */
  lua_pushinteger(L, luaL_checkinteger(L, 1) ^ luaL_optinteger(L, 2, -1));
  return 1;
}

#define LI_BITS         (sizeof(lua_Integer)*8)
#define LI_MASK(m)      (~(((lua_Integer)-1) << (m)))

static int math_bitfield (lua_State *L) {  /* aka bitshift */
  lua_Integer i = luaL_checkinteger(L, 1);
  int start = luaL_checkinteger(L, 2);
  int len = luaL_optinteger(L, 3, LI_BITS);
  if (start > 0) i = (i >> start) & LI_MASK(LI_BITS - start);
  if (len < LI_BITS) i &= LI_MASK(len);
  if (start < 0) i <<= -start;
  lua_pushinteger(L, i);
  return 1;
}

That's all!

I can live without bit operators, but bit manipulation functions
would come real handy. I'm pretty sure they would get used more
often than math.tanh(). ;-)

Bye,
     Mike

Reply | Threaded
Open this post in threaded view
|

Re: More on environments and objects

Adrian Sietsma
In reply to this post by Petite Abeille
PA wrote:
 > Excellent! Is it available publically? Got an URL? Looking forward to
see it :)

sorry, it belongs to the client i wrote i for :(
(it does some very odd things, controlled by cookies & requests)

i wrote my own multiplexer, as copas didn't suit the model i wanted, and wrapped diego's luasockets as cosockets, with yielding reads/writes. i'm (was) working on mk 2 of the multiplexer, as my own project - when i've checked out diego's latest efforts. i think he has probably beaten me to it.

i may do a stripped-out version of the proxy one day, but it's really a developement tool. it's great for debugging http servers/pages : you get to see everything (and change it on the way through).

Adrian

12