'with' statement

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

'with' statement

Dirk Laurie-2
Pascal introduced the construction

with tbl do
...
end

Inside the do block, fields of 'tbl' shadow outer variables.

This is hard to mimic exactly in Lua. Here is my attempt.

--- for _ENV in with(tbl,context) do ... end
--  * mimics Pascal `with`, i.e. not a real loop, just a do-once
--  * falls back to `context` which defaults to `_ENV`
--  * can be nested, outer level fields only visible if explicit
--  * temporarily deactivates metatable of `tbl`
local
function with(tbl,context)
  local t=tbl
  if type(t)~='table' then error("'with' expects a table, got "..type(t),2) end
  local m=getmetatable(t)
  return function()
    if t then
      t=nil
      return setmetatable(tbl,{__index=context or _ENV})
    else
      setmetatable(tbl,m)
    end
  end
end

Problem 1: Local variables defined outside the `for` stay visible.
Problem 2: I can't think of a neat way to retain the metatable of `tbl`.

Reply | Threaded
Open this post in threaded view
|

Re: 'with' statement

Soni "They/Them" L.
On 09/03/16 06:06 AM, Dirk Laurie wrote:

> Pascal introduced the construction
>
> with tbl do
> ...
> end
>
> Inside the do block, fields of 'tbl' shadow outer variables.
>
> This is hard to mimic exactly in Lua. Here is my attempt.
>
> --- for _ENV in with(tbl,context) do ... end
> --  * mimics Pascal `with`, i.e. not a real loop, just a do-once
> --  * falls back to `context` which defaults to `_ENV`
> --  * can be nested, outer level fields only visible if explicit
> --  * temporarily deactivates metatable of `tbl`
> local
> function with(tbl,context)
>    local t=tbl
>    if type(t)~='table' then error("'with' expects a table, got "..type(t),2) end
>    local m=getmetatable(t)
>    return function()
>      if t then
>        t=nil
>        return setmetatable(tbl,{__index=context or _ENV})
>      else
>        setmetatable(tbl,m)
>      end
>    end
> end
>
> Problem 1: Local variables defined outside the `for` stay visible.
> Problem 2: I can't think of a neat way to retain the metatable of `tbl`.
>
Try this:

local function with(t, e)
   return setmetatable({}, {__index=function(t,k) return t[k] or e[k] end})
end

do local _ENV = with(tbl, _ENV)
   -- stuff here
end

Doesn't solve 1 but seems much nicer than the for hack. It's also
simpler. It also supports non-tabke tbl/_ENV and metamethods in tbl.

--
Disclaimer: these emails may be made public at any given time, with or without reason. If you don't agree with this, DO NOT REPLY.


Reply | Threaded
Open this post in threaded view
|

Re: 'with' statement

Roberto Ierusalimschy
In reply to this post by Dirk Laurie-2
> Pascal introduced the construction
>
> with tbl do
> ...
> end
>
> Inside the do block, fields of 'tbl' shadow outer variables.
>
> [...]
>
> Problem 1: Local variables defined outside the `for` stay visible.

With due respect to Pascal, I think this is a good thing. It is weird
that something invisible, probably declared somewhere else, takes
precedence over something visible, declared in the same chunk.

-- Roberto

Reply | Threaded
Open this post in threaded view
|

Re: 'with' statement

Karel Tuma-2
In reply to this post by Soni "They/Them" L.
Excerpts from Soni L.'s message of 2016-03-09 11:17:52 +0100:

> local function with(t, e)
>    return setmetatable({}, {__index=function(t,k) return t[k] or e[k] end})
I'd advise just for:

do local _ENV = with_table -- hey, it reads as english too!
  ...
end

The idea is that:
1) You should never use globals in the do body, everything you need should
   come from upper closure (ie bunch of caching local = in file header)
2) "with" table statement is the only indexing we should ever do,
   so _ENV is appropiate

I think this is one of the major of uses of _ENV.
Note that it should not be overused. Usually one needs it for "table initializers"
where we need to excute full statements and mere expressions would be clumsy.

Reply | Threaded
Open this post in threaded view
|

Re: 'with' statement

Rena
In reply to this post by Roberto Ierusalimschy

On Mar 9, 2016 7:19 AM, "Roberto Ierusalimschy" <[hidden email]> wrote:
>
> > Pascal introduced the construction
> >
> > with tbl do
> > ...
> > end
> >
> > Inside the do block, fields of 'tbl' shadow outer variables.
> >
> > [...]
> >
> > Problem 1: Local variables defined outside the `for` stay visible.
>
> With due respect to Pascal, I think this is a good thing. It is weird
> that something invisible, probably declared somewhere else, takes
> precedence over something visible, declared in the same chunk.
>
> -- Roberto
>

I never thought I'd say this, but I like the way Visual Basic 6 does it:

with foo
    .x = 1
    .y = some_local_var
end with

I don't remember the exact syntax, but the important part is you still included the dot from foo.x, so that distinguished properties from other variables in outer scopes.

Reply | Threaded
Open this post in threaded view
|

RE: 'with' statement

Thijs Schreijer

> I never thought I'd say this, but I like the way Visual Basic 6 does it:
> with foo
>     .x = 1
>     .y = some_local_var
> end with
> I don't remember the exact syntax, but the important part is you still
> included the dot from foo.x, so that distinguished properties from other
> variables in outer scopes.

While reading up the thread, I had the same thought. I also like that about VB.

Thijs
Reply | Threaded
Open this post in threaded view
|

[Proposal] .name global indexing (Was: Re: 'with' statement)

Soni "They/Them" L.


On 10/03/16 05:09 AM, Thijs Schreijer wrote:

>> I never thought I'd say this, but I like the way Visual Basic 6 does it:
>> with foo
>>      .x = 1
>>      .y = some_local_var
>> end with
>> I don't remember the exact syntax, but the important part is you still
>> included the dot from foo.x, so that distinguished properties from other
>> variables in outer scopes.
> While reading up the thread, I had the same thought. I also like that about VB.
>
> Thijs
This can be implemented with .name as being a global access. But you'll
need to end your lines with a ;.

e.g.

do local _ENV = something;
   .a = 1;
   .b = 2;
end

--
Disclaimer: these emails may be made public at any given time, with or without reason. If you don't agree with this, DO NOT REPLY.


Reply | Threaded
Open this post in threaded view
|

Re: [Proposal] .name global indexing (Was: Re: 'with' statement)

Ulrich Schmidt


Am 10.03.2016 um 11:42 schrieb Soni L.:

>
>
> On 10/03/16 05:09 AM, Thijs Schreijer wrote:
>>> I never thought I'd say this, but I like the way Visual Basic 6 does
>>> it:
>>> with foo
>>>      .x = 1
>>>      .y = some_local_var
>>> end with
>>> I don't remember the exact syntax, but the important part is you still
>>> included the dot from foo.x, so that distinguished properties from
>>> other
>>> variables in outer scopes.
>> While reading up the thread, I had the same thought. I also like that
>> about VB.
>>
>> Thijs
> This can be implemented with .name as being a global access. But
> you'll need to end your lines with a ;.
>
> e.g.
>
> do local _ENV = something;
>   .a = 1;
>   .b = 2;
> end
>
There was many suggestions for language enhancements but it seems
dificult to enhance this simple and neat language. This with-thing is
one of the few ideas that make sense while trying to keep lua simple and
clean.

Right now we can code somthing in lua that looks similar to a while
statement. (We discusses this some time ago about a case statement.)

 From my point of view a while statement make sense to calculate a value
(a table reference) once and use it many times to save run time. Also
the lua source code may become easier to read in some cases.
The need to use ";" between statements is no drawback i think. I use
this already all the time. (old pascal coder habit) To use whitespaces
betwen statements is the one and only lua syntax i dont like.

Ulrich.

Reply | Threaded
Open this post in threaded view
|

Re: [Proposal] .name global indexing (Was: Re: 'with' statement)

Egor Skriptunoff-2
On Thu, Mar 10, 2016 at 3:45 PM, Ulrich Schmidt <[hidden email]> wrote:

To use whitespaces between statements is the one and only lua syntax i dont like.

-- Lua doesn't require using those pesky whitespaces between statements ;)
a=42repeat(a..a):gsub('.',print)a=a-1until#''>a

Reply | Threaded
Open this post in threaded view
|

Re: [Proposal] .name global indexing (Was: Re: 'with' statement)

Coda Highland
In reply to this post by Soni "They/Them" L.
On Thu, Mar 10, 2016 at 2:42 AM, Soni L. <[hidden email]> wrote:

>
>
> On 10/03/16 05:09 AM, Thijs Schreijer wrote:
>>>
>>> I never thought I'd say this, but I like the way Visual Basic 6 does it:
>>> with foo
>>>      .x = 1
>>>      .y = some_local_var
>>> end with
>>> I don't remember the exact syntax, but the important part is you still
>>> included the dot from foo.x, so that distinguished properties from other
>>> variables in outer scopes.
>>
>> While reading up the thread, I had the same thought. I also like that
>> about VB.
>>
>> Thijs
>
> This can be implemented with .name as being a global access. But you'll need
> to end your lines with a ;.
>
> e.g.
>
> do local _ENV = something;
>   .a = 1;
>   .b = 2;
> end
>
> --
> Disclaimer: these emails may be made public at any given time, with or
> without reason. If you don't agree with this, DO NOT REPLY.
>

At this point you might as well use _.a = 1 and then you no longer
even need a with statement.

/s/ Adam

Reply | Threaded
Open this post in threaded view
|

Re: [Proposal] .name global indexing (Was: Re: 'with' statement)

Javier Guerra Giraldez
On 10 March 2016 at 15:55, Coda Highland <[hidden email]> wrote:
> At this point you might as well use _.a = 1 and then you no longer
> even need a with statement.


that's pretty neat.  in fact it's the only suggestion in this
thread(s) that I might try at some point.  still a little sceptic
about readability, but sometimes a nice idiom is easier than trying to
come up with a sensible variable name.

--
Javier

Reply | Threaded
Open this post in threaded view
|

Re: [Proposal] .name global indexing (Was: Re: 'with' statement)

Tony Papadimitriou
In reply to this post by Egor Skriptunoff-2
I could be wrong but I believe he may have meant something different.  Consider this use of space and how it affects readability:
 
t = {}
t.
--[[
Possible
multi-line
comments
--]]
name = 'me'
print(t.name)
 
The assignment name = ‘me’ is not immediately clear it is part of the table t.
At any rate, to me this is indeed annoying, not in that I would do something like this in my own code but that I may have to deal with it in someone else’s code.
 
Sent: Thursday, March 10, 2016 5:25 PM
Subject: Re: [Proposal] .name global indexing (Was: Re: 'with' statement)
 
On Thu, Mar 10, 2016 at 3:45 PM, Ulrich Schmidt <[hidden email]> wrote:

To use whitespaces between statements is the one and only lua syntax i dont like.

-- Lua doesn't require using those pesky whitespaces between statements ;)
a=42repeat(a..a):gsub('.',print)a=a-1until#''>a

Reply | Threaded
Open this post in threaded view
|

Re: [Proposal] .name global indexing (Was: Re: 'with' statement)

Dirk Laurie-2
In reply to this post by Coda Highland
2016-03-10 17:55 GMT+02:00 Coda Highland <[hidden email]>:

> At this point you might as well use _.a = 1 and then you no longer
> even need a with statement.

I use local names consisting of one capital letter instead of _.
Idioms like "for _,value in pairs(tbl) do" cultivate a habit that
the underscore is used for return values that one does not
intend to refer to.

Reply | Threaded
Open this post in threaded view
|

Re: [Proposal] .name global indexing (Was: Re: 'with' statement)

Rena
In reply to this post by Ulrich Schmidt

On Mar 10, 2016 7:46 AM, "Ulrich Schmidt" <[hidden email]> wrote:
>
>
>
> Am 10.03.2016 um 11:42 schrieb Soni L.:
>>
>>
>>
>> On 10/03/16 05:09 AM, Thijs Schreijer wrote:
>>>>
>>>> I never thought I'd say this, but I like the way Visual Basic 6 does it:
>>>> with foo
>>>>      .x = 1
>>>>      .y = some_local_var
>>>> end with
>>>> I don't remember the exact syntax, but the important part is you still
>>>> included the dot from foo.x, so that distinguished properties from other
>>>> variables in outer scopes.
>>>
>>> While reading up the thread, I had the same thought. I also like that about VB.
>>>
>>> Thijs
>>
>> This can be implemented with .name as being a global access. But you'll need to end your lines with a ;.
>>
>> e.g.
>>
>> do local _ENV = something;
>>   .a = 1;
>>   .b = 2;
>> end
>>
> There was many suggestions for language enhancements but it seems dificult to enhance this simple and neat language. This with-thing is one of the few ideas that make sense while trying to keep lua simple and clean.
>
> Right now we can code somthing in lua that looks similar to a while statement. (We discusses this some time ago about a case statement.)
>
> From my point of view a while statement make sense to calculate a value (a table reference) once and use it many times to save run time. Also the lua source code may become easier to read in some cases.
> The need to use ";" between statements is no drawback i think. I use this already all the time. (old pascal coder habit) To use whitespaces betwen statements is the one and only lua syntax i dont like.
>
> Ulrich.
>

Using whitespace wouldn't work anyway, since it would be ambiguous:

.a = x
.b = y

Is that ".a=x;.b=y" or ".a=x.b=y"? Of course the latter is invalid, but the parser would have to look ahead an arbitrary number of tokens to discover that.

Reply | Threaded
Open this post in threaded view
|

Re: [Proposal] .name global indexing (Was: Re: 'with' statement)

Andrew Cannon
In reply to this post by Dirk Laurie-2
On 11/03/16 09:24, Dirk Laurie wrote:

> 2016-03-10 17:55 GMT+02:00 Coda Highland <[hidden email]>:
>
>> At this point you might as well use _.a = 1 and then you no longer
>> even need a with statement.
>
> I use local names consisting of one capital letter instead of _.
> Idioms like "for _,value in pairs(tbl) do" cultivate a habit that
> the underscore is used for return values that one does not
> intend to refer to.
>

Perhaps this syntax could be extended to allow already initialized table members
to be used in initialization later members, a feature which I have sometimes missed:

eg:

local t = {
        a = x1 * x2,
        b = _.a + 1
}

The '_' in this case would reference the table currently being built.

Andrew


Reply | Threaded
Open this post in threaded view
|

Re: [Proposal] .name global indexing (Was: Re: 'with' statement)

Rena

On Mar 12, 2016 9:57 AM, "Andrew Cannon" <[hidden email]> wrote:
>
> On 11/03/16 09:24, Dirk Laurie wrote:
> > 2016-03-10 17:55 GMT+02:00 Coda Highland <[hidden email]>:
> >
> >> At this point you might as well use _.a = 1 and then you no longer
> >> even need a with statement.
> >
> > I use local names consisting of one capital letter instead of _.
> > Idioms like "for _,value in pairs(tbl) do" cultivate a habit that
> > the underscore is used for return values that one does not
> > intend to refer to.
> >
>
> Perhaps this syntax could be extended to allow already initialized table members
> to be used in initialization later members, a feature which I have sometimes missed:
>
> eg:
>
> local t = {
>         a = x1 * x2,
>         b = _.a + 1
> }
>
> The '_' in this case would reference the table currently being built.
>
> Andrew
>
>
I think `b = t.a + 1` would be more natural, but I don't know how doable it is.

Reply | Threaded
Open this post in threaded view
|

[Proposal] @table and @function (Was: [Proposal] .name global indexing (Was: Re: 'with' statement))

Soni "They/Them" L.


On 12/03/16 01:02 PM, Rena wrote:

>
> On Mar 12, 2016 9:57 AM, "Andrew Cannon" <[hidden email]
> <mailto:[hidden email]>> wrote:
> >
> > On 11/03/16 09:24, Dirk Laurie wrote:
> > > 2016-03-10 17:55 GMT+02:00 Coda Highland <[hidden email]
> <mailto:[hidden email]>>:
> > >
> > >> At this point you might as well use _.a = 1 and then you no longer
> > >> even need a with statement.
> > >
> > > I use local names consisting of one capital letter instead of _.
> > > Idioms like "for _,value in pairs(tbl) do" cultivate a habit that
> > > the underscore is used for return values that one does not
> > > intend to refer to.
> > >
> >
> > Perhaps this syntax could be extended to allow already initialized
> table members
> > to be used in initialization later members, a feature which I have
> sometimes missed:
> >
> > eg:
> >
> > local t = {
> >         a = x1 * x2,
> >         b = _.a + 1
> > }
> >
> > The '_' in this case would reference the table currently being built.
> >
> > Andrew
> >
> >
> I think `b = t.a + 1` would be more natural, but I don't know how
> doable it is.
>
local t{
}

vs

local t = {
}

It does generate ambiguity when used inside the table (function call f{}
or table t{}) tho... And cannot be used as part of a return... Hmm...

@table and @function ("top level table constructor" and "current
function" respectively) could work instead, @table would be done at the
parser level and @function would be done at the bytecode level[1].

[1]: https://github.com/SoniEx2/sexlua/blob/master/FEATURES.md scroll
down to self-referential functions

--
Disclaimer: these emails may be made public at any given time, with or without reason. If you don't agree with this, DO NOT REPLY.


Reply | Threaded
Open this post in threaded view
|

Re: [Proposal] .name global indexing (Was: Re: 'with' statement)

Chris Berardi-2
In reply to this post by Rena

On Mar 12, 2016, at 9:14 AM, Rena <[hidden email]> wrote:

Using whitespace wouldn't work anyway, since it would be ambiguous:

.a = x
.b = y

Is that ".a=x;.b=y" or ".a=x.b=y"? Of course the latter is invalid, but the parser would have to look ahead an arbitrary number of tokens to discover that.


Another example would be:

    .a = x
    .b.some_function()

Is that “.a=x; .b.some_function();” or is it “.a = x.b.some_function()”?

Reply | Threaded
Open this post in threaded view
|

Re: [Proposal] .name global indexing (Was: Re: 'with' statement)

Andrew Cannon
In reply to this post by Rena
On 12/03/16 17:02, Rena wrote:

> On Mar 12, 2016 9:57 AM, "Andrew Cannon" <[hidden email] <mailto:[hidden email]>> wrote:
>>
>> On 11/03/16 09:24, Dirk Laurie wrote:
>> > 2016-03-10 17:55 GMT+02:00 Coda Highland <[hidden email]
> <mailto:[hidden email]>>:
>> >
>> >> At this point you might as well use _.a = 1 and then you no longer
>> >> even need a with statement.
>> >
>> > I use local names consisting of one capital letter instead of _.
>> > Idioms like "for _,value in pairs(tbl) do" cultivate a habit that
>> > the underscore is used for return values that one does not
>> > intend to refer to.
>> >
>>
>> Perhaps this syntax could be extended to allow already initialized table members
>> to be used in initialization later members, a feature which I have sometimes
> missed:
>>
>> eg:
>>
>> local t = {
>>         a = x1 * x2,
>>         b = _.a + 1
>> }
>>
>> The '_' in this case would reference the table currently being built.
>>
>> Andrew
>>
>>
> I think `b = t.a + 1` would be more natural, but I don't know how doable it is.
>

ok, bad example! - consider:

func( { a = x1 * x2; b = _.a + 1} )

-a


Reply | Threaded
Open this post in threaded view
|

Re: 'with' statement

Dirk Laurie-2
In reply to this post by Thijs Schreijer
Thanks to everyone who replied. The present protocol is:

--- with(tbl[,context])
-- Creates a new table with write access to `tbl` and read access to
--   `tbl` and `context`, which defaults to `_ENV`. If current _ENV
--   is local or an upvalue, it must be explicitly given as `context`.
--   If really no context is required, specify an explicit `nil`.
-- Designed for the idiom
--    do local _ENV=with(tbl)
--      ...
--    end
-- which mimics the `with` keyword in Pascal, Visual Basic etc.

The code is:

local
function with(tbl,...)
  local context = ...
  if select('#',...)==0 then context = _ENV end
  if context then
    return setmetatable({},{__newindex=tbl,
      __index=function(_,key) return tbl[key] or context[key] end})
  else
    return tbl
  end
end

2016-03-10 10:09 GMT+02:00 Thijs Schreijer <[hidden email]>:

>
>> I never thought I'd say this, but I like the way Visual Basic 6 does it:
>> with foo
>>     .x = 1
>>     .y = some_local_var
>> end with
>> I don't remember the exact syntax, but the important part is you still
>> included the dot from foo.x, so that distinguished properties from other
>> variables in outer scopes.
>
> While reading up the thread, I had the same thought. I also like that about VB.
>
> Thijs

12