lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]



On 10-Aug-05, at 2:06 PM, David Given wrote:

On Wednesday 10 August 2005 18:52, Rici Lake wrote:
[...]
C++, of course, provides the semantically odd reference type. Perhaps
that's what you were thinking of:
[...]
int& i = &ints->val;
int& j = i;  /* ILLEGAL */
int& j = &i; /* Tell the compiler *not* to dereference i */

Actually, you're not quite right there --- lines 1 and 3 are invalid, line 2
is valid. (I think that makes you exactly wrong!)

A reference can be initialised exactly once from a variable of the same type:

int& i = j; /* i becomes an alias for j */

It can then be assigned to, but the assignment is actually performed on the
target of the reference:

i = k; /* j's value is modified to be that of k's value */

You can't change the target of a reference once it's been initialised. (And
very annoying it is, too.)

I stand corrected. This is why I hardly ever use C++... I should have looked that all up before I blathered. My confusion comes from one of the weirdnesses of references: the apparent similarity of the definition and assignment syntax hide an implicit "enreference" and "dereference" operator, respectively. (And these 'operators' are inserted by the compiler.)

But the point stands: a reference is a very weird beast.

<useful example snipped>

This is phenomenally useful if you want to, say, add locks around accesses to something, particularly when combined with generics. Take a look at C++'s
smart pointers some time. This allows you to do things like:

  int value;
  locked<int> value_l = &value; /* initialisation */

  value_l = 1; /* lock, assign, unlock */
  printf("%d\n", value_l); /* lock, copy, unlock, return copy */

Very neat.

Well, beauty is in the eye of the beholder :) Many people might prefer something like:

  synchronized int value;

although that doesn't give you the option of forgetting to use the locked version :)

   aList->setval(aList->getval() + 1);

rather than the arguably more readable:

   ++aList->val;

You're not going to like me for this, but this is possible! It's a pain in the arse, though, and is usually not worth the effort. The key is to make val a
smart object which has operator++ overloaded.

I don't see how that allows 'aList' to maintain data coherency when its 'val' member is changed, unless the 'smart object' is so smart that it knows about its container. That is, of course, possible but it's not easy to see how to generalise it.

Suppose, for example, that aList is attempting to keep itself sorted. Had the implicit mutate ("assignment") operation been directed at aList in the first place, the implementation would be quite a bit clearer.


[...]
It depends
on defining a 'mutate box' primitive, which might be written ':=' or
'<-'; the semantics of 'foo <- val' would be fairly similar to the C
expression '*foo = val'.

*nods, although I'd write that last 'foo->assign(val)'*

No doubt. As I said, beauty is in the eye of the beholder :)

C++ gets away with this because it has two types of thing; scalars and
objects. (And references, but they're not relevant.) The = operator is
'assign' for scalars but 'mutate' for objects. You can overload objects but
not scalars. So this:

  Object foo;
  foo = 0;

...is a fundamentally *different* operation to this:

  Object* foo;
  foo = 0;

...despite syntactic similarities.

Sure. Furthermore, assignment and initialization are different, just to be more confusing.

Whether an object and a "scalar" are different beasts is, I guess, a philosophical question. Certainly you can override operations on "objects" which cannot be overridden on "scalars" but one might think of that as simply a restriction in the initial type environment. C++ does not, for example, guarantee that pointer assignment works like one might naively think it does; the compiler itself is allowed to insert code to, for example, maintain array boundaries in a pointer for validation purposes, in which case, it is at least conceivable that you might get a run-time exception from:

  Object *foo = 0;
  foo++;

In Lua, everything is an object (or pretends to be one),

That depends on your definition of "object". I wouldn't have said that myself (rather that in Lua every value has a definite type), but terminology is a minefield. What is clear is that Lua, like Smalltalk (at least), does not provide implicit boxes around values which can receive messages. You can only talk to the value itself.

but like Smalltalk and Java it has no 'mutate' operator.


 While one would be very useful, I do
find myself about dubious about whether it's possible to implement it and
still be able to call the result Lua!

The simple answer is: "no". If you patch the Lua core, the best you can say is that you've created a new language based on the Lua code. (At least, I think the license lets you say that.)

The more interesting answer is that the patch to create an explicit "box" type is not very big.

In fact, you could do it without patching the Lua core at all, using only a preprocessor ( :) ), by representing a box as a table with one distinguished key. The transformation would turn

  a <- *a + 7

into

  a.value = a.value + 7

or some variation on that theme. Mind you, you couldn't call the preprocessor input Lua either, but you could certainly call it a "language which can be compiled into Lua".