Macros and expressivity

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

Macros and expressivity

Mark Hamburg-4
In the discussion of the utility of macros, I got thinking about Ruby which
seems to get by without them. My Ruby is weak, so I'm going to write
examples in pseudo-Lua.

A class definition in Ruby essentially sets up an execution context in which
other code then can add methods or make other alterations. For example:

    class "Foo" from Baz
        
        function display( self ) print( self:name() ) end

        function set_name( self, newName )
            local oldName = self._name
            if oldName ~= newName then
                self._name = newName
                self:send_change_notification( "name" )
            end
        end

        function name( self )
            return self._name
        end

    end

What's more, if accessors with that pattern were common, we could somewhere
define:

    function readWriteField( publicName, privateName )
        
        local env = get_calling_environment()

        env[ publicName ] = function( self )
            return self[ privateName ]
        end

        env[ "set_" .. publicName ] = function( self, newValue )
            local oldValue = self[ privateName ]
            if oldValue ~= newValue then
                self[ privateName ] = newValue
                self:send_change_notification( publicName )
            end
        end

    end

Then our class definition becomes:

    class "Foo" from Baz
        
        function display( self ) print( self:name() ) end

        readWriteField( "name", "_name" )

    end

Another example, this time sparked by reading Rob Pike's structural regular
expressions paper, what if one could write something like:

    local filter = TextFilters.Filter

        match "(.+/n)+"             -- split into multi-line chunks
        if_find "%%A.*Bimmler"      -- check for author
        match ".+/n"                -- split into lines
        if_find "%%T"               -- find title lines
        print_text()                -- print the current line
 
    end

Each of the operations would work by adding an element to the filter chain.
Running the filter would then process the chain.

I'm sure that the various macro systems floating around could support this,
but they aren't going to be easy to construct. Ruby manages to make these
sort of constructs easy without needing to resort to macros. Let's explore
why...

The first obvious thing is that it needs to be lightweight to support
creating function closures and passing them to other functions. While it
would be good for this to be lightweight from a runtime perspective, for now
the key point is that it be lightweight from a syntactic perspective.

If we were prepared to change the meaning of "do" (and if not consider doing
this with "begin") in the cases where it wasn't at the beginning of a line
and wasn't already part of some other syntactic construct, we could say that
"do" by itself was short for "function()" and that "do" followed on the same
line by a left parenthesis was short for specifying a function with
arguments.

Secondarily, we introduce the policy that a function specified after an
existing function reference or function call -- particularly if specified
using the new syntax -- gets passed as an extra parameter. Then we could
have something like:

    class "Foo" do

        inherit_from( Baz )
        
        function display( self ) print( self:name() ) end

        function set_name( self, newName )
            local oldName = self._name
            if oldName ~= newName then
                self._name = newName
                self:send_change_notification( "name" )
            end
        end

        function name( self )
            return self._name
        end

    end

The other piece that is needed is a way to get some form of dynamic scoping.

Assume, for example, that as part of Lua's core, we track a stack of dynamic
scopes. In fact, we don't really need a stack so much as a way to get and
set the current dynamic scope table and an easy way to refer to it. For
example, we might use the @ sign and the above examples would become:

    function readWriteField( publicName, privateName )
        
        @[ publicName ] = function( self )
            return self[ privateName ]
        end

        @[ "set_" .. publicName ] = function( self, newValue )
            local oldValue = self[ privateName ]
            if oldValue ~= newValue then
                self[ privateName ] = newValue
                self:send_change_notification( publicName )
            end
        end

    end

    class "Foo" do

        inherit_from( Baz )

        function @display( self ) print( self:name() ) end

        readWriteField( "name", "_name" )

    end

The point of this message is that while all of this could be done with macro
packages, it is probably worthwhile looking to see whether Lua could get 80%
of the expressive benefit for 20% of the effort and avoid a proliferation of
competing macro-based language changes.

Mark



Reply | Threaded
Open this post in threaded view
|

Re: Macros and expressivity

Javier Guerra Giraldez
On 1/11/08, Mark Hamburg <[hidden email]> wrote:
> In the discussion of the utility of macros, I got thinking about Ruby which
> seems to get by without them. My Ruby is weak, so I'm going to write
> examples in pseudo-Lua.
>
> A class definition in Ruby essentially sets up an execution context in which
> other code then can add methods or make other alterations. For example:

i might be a little dense today (it's friday afternoon after all...);
but i don't see why you start with 'maybe we don't need macros' and go
on with classess...

-- 
Javier

Reply | Threaded
Open this post in threaded view
|

Re: Macros and expressivity

Mark Hamburg-4
on 1/11/08 1:47 PM, Javier Guerra at [hidden email] wrote:

> On 1/11/08, Mark Hamburg <[hidden email]> wrote:
>> In the discussion of the utility of macros, I got thinking about Ruby which
>> seems to get by without them. My Ruby is weak, so I'm going to write
>> examples in pseudo-Lua.
>> 
>> A class definition in Ruby essentially sets up an execution context in which
>> other code then can add methods or make other alterations. For example:
> 
> i might be a little dense today (it's friday afternoon after all...);
> but i don't see why you start with 'maybe we don't need macros' and go
> on with classess...

Sorry I wasn't clear.

The downside to macros is that they can rapidly cause a language to diverge
with a variety of competing syntax changes. Presumably we add macros to get
greater expressivity, so I wondered whether there were big wins available
without introducing all that a macro system can lead to.

Ruby arguably does better than Lua on syntactic convenience. Rails works in
part because Ruby makes it easy to express the sort of constructs that it
does. So, my investigation more or less evolved into "what would Ruby
constructs look like with a more Lua-ish syntax" and "what would one have to
do to Lua to support this".

Mark



Reply | Threaded
Open this post in threaded view
|

Re: Macros and expressivity

Asko Kauppi
In reply to this post by Mark Hamburg-4

Chaining functions to each other can be done already, by returning a function that then again gets called, until the end says () or similar.

I've used this somewhere, guess it was a 'switch' implementation. But you're right, the "function()" token itself makes it a bit uneasy on the eyes.

Here's two lines from luaSub's (one of those dreaded 'macro' packages :) tests/test-do.lua:

<<
f do print "not" end function() print "quite" end do print "yet!" end
-- f ( function() print "not" end ) ( function() print "quite" end ) ( function() print "yet!" end )
<<

First line is the modified syntax and the latter is what is fed to Lua core.

'f' is defined as: function f(x) x() end

This needs two changes in the Lua parser: allowing 'do' as 'function ()' and allowing function blocks as paranthesis-less args, just like func"" and func{} already are.

-asko


Mark Hamburg kirjoitti 11.1.2008 kello 23:35:

...

Another example, this time sparked by reading Rob Pike's structural regular
expressions paper, what if one could write something like:

    local filter = TextFilters.Filter

        match "(.+/n)+"             -- split into multi-line chunks
        if_find "%%A.*Bimmler"      -- check for author
        match ".+/n"                -- split into lines
        if_find "%%T"               -- find title lines
        print_text()                -- print the current line

    end

Each of the operations would work by adding an element to the filter chain.
Running the filter would then process the chain.

I'm sure that the various macro systems floating around could support this, but they aren't going to be easy to construct. Ruby manages to make these sort of constructs easy without needing to resort to macros. Let's explore
why...

...

Reply | Threaded
Open this post in threaded view
|

Re: Macros and expressivity

Miles Bader-2
Asko Kauppi <[hidden email]> writes:
> This needs two changes in the Lua parser:  allowing 'do' as 'function
> ()' and allowing function blocks as paranthesis-less args, just like
> func"" and func{} already are.

Incidentally, any syntactic reason not to allow other "parenthesis-less"
calls?

E.g., you can use:  fun 'blargh'
sometimes I'd kinda like to be able to use:  fun 3
as well...

-miles

-- 
[|nurgle|]  ddt- demonic? so quake will have an evil kinda setting? one that
            will  make every christian in the world foamm at the mouth?
[iddt]      nurg, that's the goal

Reply | Threaded
Open this post in threaded view
|

Re: Macros and expressivity

Mark Hamburg-4
In reply to this post by Asko Kauppi
on 1/11/08 2:54 PM, Asko Kauppi at [hidden email] wrote:

> 
> Chaining functions to each other can be done already, by returning a
> function that then again gets called, until the end says () or similar.
> 
> I've used this somewhere, guess it was a 'switch' implementation. But
> you're right, the "function()" token itself makes it a bit uneasy on
> the eyes.
> 
> Here's two lines from luaSub's (one of those dreaded 'macro'
> packages :) tests/test-do.lua:
> 
> <<
> f do print "not" end function() print "quite" end do print "yet!" end
> -- f ( function() print "not" end ) ( function() print "quite" end )
> ( function() print "yet!" end )
> <<
> 
> First line is the modified syntax and the latter is what is fed to
> Lua core.
> 
> 'f' is defined as: function f(x) x() end
> 
> This needs two changes in the Lua parser:  allowing 'do' as 'function
> ()' and allowing function blocks as paranthesis-less args, just like
> func"" and func{} already are.

For your example, I think you need:

    f = function(x) x() return f end

But yes, Lua is really close to being able to support this natively.

Of course, the moment one starts using this to create control constructs,
you get into the messy question of what does a "return" inside the code
mean.

Turning to my class examples, without the sugar but with the support for
parenthesis-free passing of function arguments we get:

    class "Foo" function()

        inherit_from( Baz )

        function display( self ) print( self:name() ) end

        readWriteField( "name", "_name" )

    end

This is an improvement over:

    class "Foo" ( function()

        inherit_from( Baz )

        function display( self ) print( self:name() ) end

        readWriteField( "name", "_name" )

    end )

But still looks strange. Furthermore, "function()" is a lot more to stick on
the end of a line than "do".

The other reason I started adding in the notion of a standard dynamic scope
was that it avoids having a global references detector complain about the
declaration of "display". This could actually be handled as:

    class "Foo" ( function()

        inherit_from( Baz )

        function _S:display() print( self:name() ) end

        readWriteField( "name", "_name" )

    end )

But as with the explicit function creation, the use of a magic global
variable feels odd compared to using special syntax.

(On a tangential note, it might be interesting to require special syntax for
accessing the global environment -- e.g., global symbols would have to be
prefixed with "$". Variables would be "local by default" except that you
would still have to declare them so essentially they would be "undeclared by
default".)

Mark



Reply | Threaded
Open this post in threaded view
|

Re: Macros and expressivity

Fabien-3
In reply to this post by Mark Hamburg-4

Mark Hamburg wrote:
The downside to macros is that they can rapidly cause a language to diverge
with a variety of competing syntax changes. Presumably we add macros to get
greater expressivity, so I wondered whether there were big wins available
without introducing all that a macro system can lead to.

I agree that operating at runtime (RT) rather than compile-time (CT) is often simpler, that it translates into better maintainability, and that it is an excellent reason to prefer RT when applicable. If I understood correctly, you consider adding/changing some syntactical features in Lua, so that many desirable syntax extensions become doable at RT rather than CT.

However, I can't see how RT syntax tweaks would lead to less divergences and incompatibilities than CT ones. Quite the opposite, when CT and RT stages are clearly separated, and syntax control exclusively belongs to CT, there is a red line to cross purposefully before committing syntax rape; you have to be aware of what you're doing, and you have a last chance to ponder whether it's worth it. Not so with "fluid APIs": the frontier between regular functions and syntax hacking becomes dangerously blurry, and you might cross it without even noticing.

One of the biggest problems with extensible syntax is composition: you want several, independently designed extensions to work well together in your source files, just as for regular libraries. This issue is far from being solved, and that's why you want to avoid tying a library with syntax extensions: you'd lose a lot of your library's modularity, which is what the very concept of library is all about.

I'm not sure that RT syntax tweaks would be easier to get right than CT Meta-Programming within a correctly designed framework. Moreover, CTMP leaves the door open to static analysis, so you can be warned about dangerous and/or incompatible extensions sooner in the development process. Similarly, with CTMP, an incorrect syntax is normally spotted at CT, and a thorough analysis can be done to explain the error clearly; I'd guess that erroneous uses of RT syntax extensions could be awfully trickier to fix. RTMP doesn't seem easier than CTMP *for syntax extensions of comparable elegance and robustness*. A lot of metaprogramming complexity seems intrinsic, not accidental, and I'm afraid that RTMP merely shifts part of the burden from the extension author to the users. Lowering the barrier to entry into meta-programming doesn't look like a great idea IMO.

About Ruby: I'm definitely not obsessed with performances and benchmarks and whatnot, especially in a language that integrates so tightly with C as Lua, but Ruby is known for its embarrassingly bad performances. That might be somehow linked to its extreme dynamism (the same could be written about Io). Lua would be useless for many of its users, if its performances were on par with Ruby's.

That's it with generalities :) Now about your proposals: most users agree that Lua deserves a less cumbersome syntax for closures. After some experience with Metalua's terser (and Ruby-like!) alternative syntax, I'd say that in addition to lightweight closure syntax, you want to get rid of the _expression_/statement distinction. Your proposal to hack "do", with an interpretation that depends on line breaks, doesn't feel very lua-ish, but as shown about twice a month in the list's traditional Syntax Extension Proposal Troll, syntax is surprisingly hard to get right.

Dynamic scoping usually produces subtle bugs, and is a terrible pain in the couple of old languages that inherited it: I don't thing it woud cause less havoc than a clearly separated CTMP stage. Maybe a static operator inspired by Pascal's "with" could do the job in a slightly cleaner way, but that construct is quite infamous, in Pascal, as a wart that leadto subtle bugs. My biased preference goes to a clear, explicit, readable definition of how those implicit parameters (the current class/instance in your examples) are handled, and a clear segregation of CTMP code from RT code. RTMP is friendlier to quick & dirty hacks, but you probably don't want dirty hacks in your language's semantics.

Your example with TextFilters is quite interesting. Unless I missed something, it boils down to "Monads are awesome, let's make them readable in our language". It's been done in Haskell, and the result is amazingly powerful indeed, although not intuitive to newcomers. Definitely, having a nice way to define and use monads in Lua would be, at least, an interesting experiment. It might fail, however, due to the lack of static type-checking: many monads are intrinsically tricky, and are usable in Haskell mainly thanks to the type checker which catches so many mistakes. I'm fairly confident that monad transformers would be just unusable without static type inferrence.

CTMP is not necessarily about monkey-patching an unstructured stream of source code: if it offers the right level of abstraction to its users, proper error detection mechanisms, and idioms that help structuring one's code in a readable maintainable way, it's actually cleaner and safer than RTMP. It generates a lot of noisy discussions about gratuitous superficial syntax tweaks, as predicted by the bikeshed theorem (http://bikeshed.com), but that's a tiny part of what it's all about. MP is intrinsically hard; something that disguises it as simple is cheating on you, and will bite you when you least expect it.

Finally, if you really want to figure out how Lua could benefit from a more RTMP-friendly syntax, my 2c advice is: put on your robe and your hacker's hat, get metalua, and try your ideas! :)


-- Fabien.
Reply | Threaded
Open this post in threaded view
|

RE:Need standard kernel supported class in Lua, was: Macros and expressivity

Grellier, Thierry
In reply to this post by Mark Hamburg-4
In that case, I don't understand why then not returning a class table to
benefit Lua unaltered and compact syntax?

    class "Foo"
        :inherit_from( Baz )
        :readWriteField( "name", "_name" )

    function display( self ) print( self:name() ) end

    or, if you want scoping

    function Foo:display() print( self:name() ) end

Yet I think that Lua not having a standard class is in the end a
weakness
Despite academically one would enjoy defining a class system (well no
reason why this can't always the case), this is quite a show stopper.
It is a "very big loss" of time defining what everything else has and
then explaining how this prevent from reusing other tools with
confidence or taking time to explain why this class definition is better
than another one, or just this syntax is better...
They key reason Python was selected here (Lua can be used only for
configuration, but is one of several possibilities) was "there is no
class in Lua" (hear standard). All other argument for Lua weren't strong
enough:
- the speed advantage of Lua doesn't help (if we would really want speed
we wouldn't use script and Lua speed can actually be made really bad
with bad class definition to cache method lookup). 
- given the trend, embedded systems are not that resource constrained.
Imagine that embedded systems start reusing cell phone socs which are
made very cheap given volumes. And well, we are not using it in embedded
context...
- Even co-routines didn't help not selecting Python although they were a
lot more useable than generators and posix thread.
- combine this with absence of bitwise of operators...


was:
Turning to my class examples, without the sugar but with the support for
parenthesis-free passing of function arguments we get:

    class "Foo" function()

        inherit_from( Baz )

        function display( self ) print( self:name() ) end

        readWriteField( "name", "_name" )

    end

This is an improvement over:

    class "Foo" ( function()

        inherit_from( Baz )

        function display( self ) print( self:name() ) end

        readWriteField( "name", "_name" )

    end )

But still looks strange. Furthermore, "function()" is a lot more to
stick on
the end of a line than "do".

The other reason I started adding in the notion of a standard dynamic
scope
was that it avoids having a global references detector complain about
the
declaration of "display". This could actually be handled as:

    class "Foo" ( function()

        inherit_from( Baz )

        function _S:display() print( self:name() ) end

        readWriteField( "name", "_name" )

    end )

But as with the explicit function creation, the use of a magic global
variable feels odd compared to using special syntax.

(On a tangential note, it might be interesting to require special syntax
for
accessing the global environment -- e.g., global symbols would have to
be
prefixed with "$". Variables would be "local by default" except that you
would still have to declare them so essentially they would be
"undeclared by
default".)

Mark




Reply | Threaded
Open this post in threaded view
|

RE:Need standard kernel supported class in Lua, was: Macros and expressivity

Eike Decker-2
Zitat von "Grellier, Thierry" <[hidden email]>:

> (On a tangential note, it might be interesting to require special syntax
> for
> accessing the global environment -- e.g., global symbols would have to
> be
> prefixed with "$". Variables would be "local by default" except that you
> would still have to declare them so essentially they would be
> "undeclared by
> default".)

I think it would bloat the code with quite lot's of $ signs (imagine all the
global tables containing the functions you need everywhere - io, table, string
etc.). 
However, something similar like what you suggest this could already be done by
assigning a nearly empty environment table to a function:

----
function f ()
  local print = G.print
  print(x) -- error is thrown here
end

local env = {G = _G}
setmetatable(env,{
  __index = function(t,key)
    error("accessing nonexisting variable named "..tostring(key),2)
  end, 
  __newindex = function(t,key,val)
    error("implicit variable declaration forbidden",2)
  end}
)

setfenv(f,env)

f() -- throws error on line 3 which says that x does not exist

----

It's a bit inconvenient because the environment needs to be assigned to the
function before it works. However there could be an automatism using metatables
which will assign this special function environment automaticly or by calling a
function:

----
local env = {G = _G}
setmetatable(env,{
  __index = function(t,key)
    error("accessing nonexisting variable named "..tostring(key),2)
  end, 
  __newindex = function(t,key,val)
    error("implicit variable declaration forbidden",2)
  end}
)
function safeenv (func)
  return setfenv(func,env)
end

-- method 1:

foo = safeenv(function () 
  -- uncomfortable somehow 
end)

-- method 2:

safebox = {}
setmetatable(safebox, {
  __newindex = function (t,k,func) rawset(t,k,setfenv(func,env)) end
})

function safebox.foo () 
 -- all functions in safebox will be assigned 
 -- the env function environment
end


However, I would rather want to have an editor which knows the scope and would
color the typed names depending on their local/global type. 
Or a lua compiler flag could make the compiler complain about accessing global
values without using the _G table. 
Or do I overlook a case where the scope can only be determined during runtime? 

Eike

Reply | Threaded
Open this post in threaded view
|

Re: Need standard kernel supported class in Lua, was: Macros and expressivity

Luiz Henrique de Figueiredo
> Or a lua compiler flag could make the compiler complain about accessing global
> values without using the _G table. 

See globals.lua and trace-globals.lua in the test directory in the standard
Lua source distribution. --lhf

Reply | Threaded
Open this post in threaded view
|

Re: Need standard kernel supported class in Lua, was: Macros and expressivity

steve donovan
In reply to this post by Grellier, Thierry
On Jan 14, 2008 11:42 AM, Grellier, Thierry <[hidden email]> wrote:
> In that case, I don't understand why then not returning a class table to
> benefit Lua unaltered and compact syntax?

If you want Python-style class declarations, you can do that simply enough:

function class(body)
    local mt = {G = _G}
    mt.__index = mt
    setfenv(body,mt)
    body()

    function mt.new()
        local self = {}
        setmetatable(self,mt)
        return self
    end

    return mt
end

animal = class(function()
    function speak(self)
        G.print ('growl '..self:get_name())
    end

    function set_name(self,s)
        self.name = s
    end

    function get_name(self)
        return self.name
    end
end)

a = animal.new()
a:set_name 'fido'
a:speak()

With a little extra macro sugar, this is a quite painless syntax!

Multiple inheritance can be implemented with a little extra trouble,
and you can probably get a proper global context as well for the
methods, as well.

steve d.

Reply | Threaded
Open this post in threaded view
|

Re: Need standard kernel supported class in Lua, was: Macros and expressivity

Fabien-3
In reply to this post by Grellier, Thierry
Classes are the most common way to define APIs, i.e. to make software components interoperable (at least in theory: in practice, many libraries aren't as reusable as they pretend to be, because reusablility is genuinly hard). So indeed, that's a place where consensual conventions to follow are most important.

Lua has always chosen not to endorse any convention; this explains its most striking strengths as well as its most crippling weaknesses, and the lack of a *standard* class model certainly is one of the worst instances of this issue. That's the untold issue of many mailing list frustrations, which go like:

- "Hello, I'm $NEWBIE, I want to do $FOOBAR in Lua"
- "You  encode it in a compact way, like $THIS, or like $THAT"
- "Yes but what's the standard way?"
- "There's no standard, pick your favored approach. We believe in metamechanisms and user choices".

Then $NEWBIE walks away, frustrated that his request hasn't been understood, let alone satisfied: he wanted an officially sanctioned way to do $FOOBAR, the genuine one best way, the one that would free him from low level programming considerations (as defined by Perlis: "A programming language is low level when its programs require attention to the irrelevant").

A language is as much of a social entity as a technical one. A technically dull language that gathers considerable library-writing and -sharing forces, such as Java, is superior to an extremely neat language with little established conventions to reassure people, not enough libraries, insufficient interoperability between the existing ones, or portability issues. Being semantically cleaner and marginally faster than, say, Python, is no match against the lack of an established social environment, with conventions, comprehensive code base, *standard* ways to grow that base, no need to ask oneself preposterous questions such as "what OO model will I pick for my XML parser?", etc.

Lua seems to have chosen to assume its non-social nature, and strives to be the best generic language to put over YOUR set of libs and conventions. That's a niche, but a coveted one, and Lua does an excellent job at occupying it.

The regular efforts to build a platform (language + social features such as larger *official* std lib) around Lua systematically fail. Some of the reasons might be:

- The Lua team never saw fit to publicly endorse any of these initiatives. Again, this is all about social issues, not technology, and some sort of official seal of approval is just mandatory.

- Lua has no killer feature strong enough to justify switching from Python, or some other wannabe-Python language.

- All attempts have been undertaken while largely underestimating the required effort, or under the false assumption that the technical part of the challenge what the trickiest one.

-- Fabien.

Reply | Threaded
Open this post in threaded view
|

Re: Need standard kernel supported class in Lua,

Robert Raschke-2
Designing a good API is hard regardless of whether you use OO or not.
It is the design that is hard, not the coding.

Lua's lack of a standard class mechanism doesn't make that design any
harder or easier, unless you skip the design stage and jump straight
to coding.  For the latter, I would recommend Perl or Java, they're
perfect for that kind of approach and come with enormous amounts of
example code you can take as your guide. ;-)

Robby


Reply | Threaded
Open this post in threaded view
|

Re: Need standard kernel supported class in Lua, was: Macros and expressivity

Ben-2
In reply to this post by Fabien-3
That's why LOOP exists.  Just send people on over there, and it's
cool.  It's not like the mechanisms are already there.  Considering
all the hoops you have to jump through in other languages, and also
considering the "syntax weirdity" of Lua already, compared a C-like
language, what's a couple parentheses in your class definitions?

And isn't LOOP maintained by Tecgraf?  That seems the closest you can
get to official endorsement.  It's a good system, it works, so why do
people keep complaining about a lack of class syntax?  Or is it just
not that well-known?

Cheers,
Ben

Reply | Threaded
Open this post in threaded view
|

Re: Need standard kernel supported class in Lua, was: Macros and expressivity

Javier Guerra Giraldez
my two cents:

- most 'quick & dirty' programs doesn't need OOP

- the obj:mth() syntactic sugar trivially handles encapsulation and polymorphism

- most OOP simple (and not so simple!) designs doesn't need inheritance.

- if you need a full inheritance class system, there are several to
pick from. some of them really sophisticated.

- no matter how a class system is implemented, if it uses the
obj:mth() sugar for method calls, it can interoperate with other
systems.

so.... why the fuss?


-- 
Javier

Reply | Threaded
Open this post in threaded view
|

Re: Need standard kernel supported class in Lua, was: Macros and expressivity

Luís Eduardo Jason Santos-2
100% agreed.

Lua doesn't need a standard 'class' system. There is the danger of locking people into it (a mild danger) but there is also the danger of bringing in things that are not necessary to many people.

If something is going to change in this direction, it will probably be one of those clever devices one of the Three Creators sometimes devise to solve a thousand problems with some simple concept, a handful of code and almost no overhead ;-).

OOP is just too messy, too complex a problem to fit this definition. It should be a *use recommendation*, not a language construct. And s such, should come in several flavors.

On Jan 14, 2008 7:29 PM, Javier Guerra < [hidden email]> wrote:
my two cents:

- most 'quick & dirty' programs doesn't need OOP

- the obj:mth() syntactic sugar trivially handles encapsulation and polymorphism

- most OOP simple (and not so simple!) designs doesn't need inheritance.

- if you need a full inheritance class system, there are several to
pick from. some of them really sophisticated.

- no matter how a class system is implemented, if it uses the
obj:mth() sugar for method calls, it can interoperate with other
systems.

so.... why the fuss?


--
Javier



--
Luís Eduardo Jason Santos
Reply | Threaded
Open this post in threaded view
|

Re: Need standard kernel supported class in Lua, was: Macros and expressivity

Thiago Bastos-2
2008/1/14, Luís Eduardo Jason Santos <[hidden email]>:
100% agreed.

Lua doesn't need a standard 'class' system. There is the danger of locking people into it (a mild danger) but there is also the danger of bringing in things that are not necessary to many people.

If something is going to change in this direction, it will probably be one of those clever devices one of the Three Creators sometimes devise to solve a thousand problems with some simple concept, a handful of code and almost no overhead ;-).

I couldn't agree more (with both e-mails).

OOP is just too messy, too complex a problem to fit this definition. It should be a *use recommendation*, not a language construct. And s such, should come in several flavors.

Whether OOP is messy or not, I just think it is completely unnecessary for most applications. In most scenarios where Lua is used as an extension language for applications written in, say, C++, you want the extension mechanism to be as simple as possible. If you want to design big fancy OOP systems, you do it in C++ and then expose your system to Lua.

Some people have argued they could see no killer feature in Lua that could justify not using Python. Well, for me it works the opposite way. None of Python's features are attractive enough to justify the extra burden and complexity of embedding Python instead of Lua.

Lua's current design is very good for the purpose it was created for, being an "extensible extension language".

-- Thiago

On Jan 14, 2008 7:29 PM, Javier Guerra < [hidden email]> wrote:
my two cents:

- most 'quick & dirty' programs doesn't need OOP

- the obj:mth() syntactic sugar trivially handles encapsulation and polymorphism

- most OOP simple (and not so simple!) designs doesn't need inheritance.

- if you need a full inheritance class system, there are several to
pick from. some of them really sophisticated.

- no matter how a class system is implemented, if it uses the
obj:mth() sugar for method calls, it can interoperate with other
systems.

so.... why the fuss?


--
Javier



--
Luís Eduardo Jason Santos

Reply | Threaded
Open this post in threaded view
|

Re: Need standard kernel supported class in Lua, was: Macros and expressivity

Eric Tetz
On Jan 14, 2008 1:04 PM, Ben <[hidden email]> wrote:
> That's why LOOP exists.  Just send people on over there

LOOP's greeting to the newbie:

"LOOP [..] is a set of packages for supporting different models of
object-oriented programming in the Lua language. [..] The models
provided by LOOP are not intended to define a standard for the Lua
language. Instead, they are examples [...]"

It then describes 5 different OO models. I think your hypothetical
newbie is going to go away more confused than ever. :)

BTW, never having seen the LOOP site before, I thought this statement
sounded a bit like a post-facto rationalization: "lack of a standard
object model avoids the use of a peculiar model instead of models
customized for particular needs, like simplicity, flexibility or
performance."

It seems to me that a built-in model wouldn't require you to choose,
because you could meet all those requirements at once. In particular,
a built-in solution wouldn't force you to choose between flexibility
and performance.

For instance, C++ inheritance can be as simple as 'struct Foo : Bar
{}', while at the same time allowing you to get progressively more
complicated (multiple inheritance, interface inheritance, access
control, etc.) without sacrificing performance.

I also have to agree that the lack of a built-in model does tend to
discourage OOP, at least at my workplace. If we're going to design our
APIs in an OO manner, someone needs to design (or choose) the class
mechanisms we will use. The path of least resistance is just to avoid
it altogether or to have numerous ad hoc mechanisms in use.

I'm not arguing that Lua should build-in support beyond the mechanisms
already in place, just that I can see where users like Fabian are
coming from.

Cheers,
Eric

Reply | Threaded
Open this post in threaded view
|

Re: Need standard kernel supported class in Lua, was: Macros and expressivity

Javier Guerra Giraldez
In reply to this post by Luís Eduardo Jason Santos-2
On 1/14/08, LuÃs Eduardo Jason Santos <[hidden email]> wrote:
> OOP is just too messy, too complex a problem to fit this definition. It
> should be a *use recommendation*, not a language construct. And s such,
> should come in several flavors.

i didn't want to say that, partly because it could easily degenerate
into a religious war; and also because everybody that's asking for OOP
have obviously made his/her mind on this subject, and if somebody just
disregards it, then the rest of the comment is ignored.

i don't think OOP isn't useful, it's of course overrated, but it's
certainly a neat abstraction for many problems.

but...

when somebody say "but lua doesn't have objects!", and the answer is
"build your own!", they go on and build something big and bloated.
why? i think that's because they're trying to bring in all everything
that their favourite language does.

but...

when a given abstraction is included into the (relatively slow moving)
core language, it _have_ to be all things to all people.

when it's just a code writing convention (like how the obj:mth() sugar
encourages), it can be really really simple when you don't need extra
bells and whistles.  just creating a fixed metatable with __index=(the
module table) is enough for a huge number of projects.

'blessing' a given class package would be nice for some projects, but
irrelevant for the vast majority of them.

-- 
Javier


Reply | Threaded
Open this post in threaded view
|

RE: Need standard kernel supported class in Lua, was: Macros and expressivity

Jerome Vuarand-2
In reply to this post by Eric Tetz
Eric Tetz wrote:
> "LOOP [..] is a set of packages for supporting different models of
> object-oriented programming in the Lua language. [..] The models
> provided by LOOP are not intended to define a standard for the Lua
> language. Instead, they are examples [...]"   
> 
> It then describes 5 different OO models. I think your hypothetical
> newbie is going to go away more confused than ever. :) 
> 
> [...]
> 
> For instance, C++ inheritance can be as simple as 'struct Foo : Bar
> {}', while at the same time allowing you to get progressively more
> complicated (multiple inheritance, interface inheritance, access
> control, etc.) without sacrificing performance.   

C++ has structs, the dot syntax (which ends up being a syntactic sugar
over C symbols) and virtual method tables. Lua has tables, the colon
syntactic sugar, and metatables. That's all you need to do OOP. From
that you can build progressively more complicated models.

As you mentionned, LOOP implements 5 such models, and they are presented
as progressive refinements. Just use the simplest one until you feel you
need more features. At least that's the advice I'd give to someone
asking for Lua OOP on IRC.

> I also have to agree that the lack of a built-in model does tend to
> discourage OOP, at least at my workplace. If we're going to design
> our APIs in an OO manner, someone needs to design (or choose) the
> class mechanisms we will use. The path of least resistance is just to
> avoid it altogether or to have numerous ad hoc mechanisms in use.    

Wouldn't the presence of a built-in OOP model encourage OOP ? Is that
desirable ? I personnaly prefer Lua as paradigm-agnostic, without
preventing users to use the paradigm they like.


123