[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Re: Lightweight function syntax (again) Re: [ANN] Lua 5.2.0 (alpha-rc2) now available
- From: Pierre-Yves Gérardy <pygy79@...>
- Date: Tue, 23 Nov 2010 14:01:42 +0100
-- Pierre-Yves
On Mon, Nov 22, 2010 at 16:06, Mark Hamburg <mark@grubmah.com> wrote:
> On Nov 21, 2010, at 9:52 PM, steve donovan wrote:
>
>> But there is a widespread idea that this is (a) redundant and (b) not
>> in the Lua keyword-driven spirit. I think it makes the functional
>> style of programming less 'noisy' - the issue is not 'typeability' so
>> much as 'readability'. I understand that this belief isn't shared by
>> everyone ;)
> I also think it's a lot more about readability than about typeability and that puts significant back pressure on any efforts to move toward more special characters. The issue at hand -- and one clearly up for debate -- is whether the existing syntax ends up overwhelming the meaningful bits in some usages.
Readability is also what I'm after, but I'd argue that special
characters improve readability by making the functions stand out. One
of my gripes with the current syntax is that funcall(args) and
function(args) are too similar to distinguish at a glance. As you also
pointed, in callback heavy code, the function keyword tend to
dominates, and decreases the SNR.
> I agree that the pressure tends to have to do with what sort of programming one is trying to engage in with more functional styles being more challenged by Lua's otherwise very pleasant syntax -- though it is also a challenge for idioms common in Smalltalk and Ruby. For me, it's an issue in trying to build up reactive systems.
In my case, it is about tying callbacks to keypresses and events in games.
state "menu" :keyconfig
"%arrows" :held @k,dt => hero:move(k,dt)
" " :pressed @ => hero:shoot()
:held @_,dt => hero:accumulateEnergy(dt)
:released @ => hero:releaseBigBlow()
"escape" :pressed @ do
makesound()
state.goto "pause"
end
()
state "menu" :keyconfig
"%arrows" :held (function(key,dt) hero:move(key,dt) end)
" " :pressed (function() hero:shoot() end)
:held (function(_,dt) hero:accumulateEnergy(dt) end)
:released (function() hero:releaseBigBlow() end)
"esc" :pressed (function()
makesound()
state.goto "pause"
end)
()
state "menu" :keyconfig
"%arrows" :held |k,dt => hero:move(k,dt)|
" " :pressed |=> hero:shoot()|
:held |_,dt => hero:accumulateEnergy(dt)|
:released |=> hero:releaseBigBlow()|
"escape" :pressed | do
makesound()
state.goto "pause"
|
()
------------------
Regarding the syntax, it has to take a lot of parameters into account,
both technical, semantical (do we allow statements?) and aesthetical.
## The recursive descent nature of the parser, which has a lot of
benefits like speed and small memory footprint, imposes a lot of
constrains:
* A unique token must start the sequence. Single ASCII tokens
available include \ ~ @ ! ? & | and $. `[` is fine too except if we
want to be able give lambdas the same syntactic diet as strings and
table when passed as a single argument to a function.
* One must delimit the argument list in one way ot another in order to
avoid ambiguities when no arguments are defined.
|args => expression| -- and
|args do statements end|
are both ok, but the two closing tokens in the second case are
somewhat redundant. Getting rid of either would result in an imbalance
which is unconsistent with the rest of the language.
* A closing token is mandatory.
somefunc @ a,b => a < b :somemethod()
would be ambiguous otherwise.
## On the semantics side, do we want to have
a) full anonymous functions with multiple statements but a terser syntax ?
*OpeningToken* arglist *SeparatorToken* {statement}
[*ReturnToken* expression] *ClosingToken*
b) lambda, returning just one expression (no return token)?
1) *OpeningToken* arglist *SeparatorToken* [ expression] *ClosingToken*
or
2) *OpeningToken* arglist *ReturnToken* [ expression] *ClosingToken*
c) a and b1 with identical tokens.
d) a and b2 with identical tokens.
## I can't find a way to get c) with while keeping the LL(1) parser,
or even an LL(n) parser for any finite value of n. I don't think it
would be possible to distinguish between an initial statement and a
value to be returned.
If we want both, we must thus use different tokens, either initially
or as a separator.
@ x,y (x*y) -- or Luiz's `\ x,y (x*y)`, even though I find that `@`
stands out better.
@ x,y [statements =>expression]
would be valid.
## For d) we get this:
| args => value |
| args : statements =>expression |
This is my favourite so far; clean and distingtive. The context is
different enough from method calls to use the column IMO, but any
other token can be used of course.
Kind regards,
-- Pierre-Yves