Another option for closing variables

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

Re: Another option for closing variables

pocomane


On Mon, Jul 15, 2019, 11:47 AM Sergey Kovalev <[hidden email]> wrote:

for auto,defer in scope() do
  defer(function() print"defer" end)
  local f=auto(io.close){ io.open "scope54.lua"}
  print(f:read())
end

I think this is enough readable and looks nice.

This is still way less readable than

local <toclose> file, err = io.open("myfile")
-- ...

However, you can simplify scope() a bit [1], allowing to write

for finally_close in scope() do
  local file, err = io.open("myfile", "r")
  finally_close( file )
  -- ... 
end

That is clean enough to not make me miss <toclose>. I can also ignore the fact that the 4th iterator parameter itself is implementated as a <toclose> variable.

But with the last commits we got constant propagation, so I really want <const>. And if we already have annotatations probably <toclose> is worth too.

The annotation system is a considerable addition to the language and it may seem an overkill for a single use case. But it starts to make sense when multiple issues are accounted by it.

Who would endorse the metatables if them were used only for __gc?

pocomane

[1] E.g.:

local function scope()
  local obj = {}

  local function mark_to_close( o )
      obj[1+#obj] = o
  end
  
  local function closing()
      for i = 1, #obj do
          local m = getmetatable( obj[i] )
          if m and m.__toclose then
              m.__toclose( obj[i] )
          end
      end
  end

  local function iter( n, c )
      if c == nil then return mark_to_close end
      return nil
  end
  
  return iter, nil, nil, setmetatable({},{__close=closing})
end

Reply | Threaded
Open this post in threaded view
|

Re: Another option for closing variables

Sergey Kovalev
вт, 16 июл. 2019 г. в 08:31, pocomane <[hidden email]>:

>
> However, you can simplify scope() a bit [1], allowing to write
>
> for finally_close in scope() do
>   local file, err = io.open("myfile", "r")
>   finally_close( file )
>   -- ...
> end
>
> That is clean enough to not make me miss <toclose>. I can also ignore the fact that the 4th iterator parameter itself is implementated as a <toclose> variable.
Yes. This hides this inconsistent syntax.

// lparser.c
static void localstat (LexState *ls) {
  /* stat -> LOCAL NAME {',' NAME} ['=' explist]
           | LOCAL *toclose NAME '=' exp */
  if (testnext(ls, '<'))
    attriblocalstat(ls);
  else
    commonlocalstat(ls);
}
static void attriblocalstat (LexState *ls) {
  Vardesc *var;
  TString *attr = str_checkname(ls);
  testnext(ls, '>');
  var = new_localvar(ls, str_checkname(ls));
  checknext(ls, '=');
  exp1(ls);
  adjustlocalvars(ls, 1);
  if (strcmp(getstr(attr), "const") == 0)
    var->ro = 1;  /* set variable as read-only */
  else if (strcmp(getstr(attr), "toclose") == 0)
    tocloselocalstat(ls, var);
  else
    luaK_semerror(ls,
      luaO_pushfstring(ls->L, "unknown attribute '%s'", getstr(attr)));
}
There is no way to extend this attributes only hard coded const and toclose

You can not write "local <const> a,b"
or if you write "local <const> a={a=1,b=2}" you can modify constant a:
"a.a=10 a.b=20"
I prefer to use runtime constants:

function const(c) return setmetatable({},{__index=c,
    __newindex=function(t,n,v) error "const is read only" end
}) end

local C=const { pi=3.14, e=2.71, B0=1, B1=2, B3=4, B4=8, print=print,
P=const{ x=1, y=2 } }
C.B1=4 -- errror const is read only

I can declare group of constants, can pass this group of constants
into functions.

> But with the last commits we got constant propagation, so I really want <const>. And if we already have annotatations probably <toclose> is worth too.
What does it mean exactly?
local<const> A=1
local B,C=A,A*A
local function f1(a) a=2 return a end
f1(A)
local function f2() A=2 end -- propagation means only this?


>
> The annotation system is a considerable addition to the language and it may seem an overkill for a single use case. But it starts to make sense when multiple issues are accounted by it.
In state it now exists they are little more than useless.
There is no annotation system. There are two hard coded cases.

> [1] E.g.:
> local function scope()
>...
Yes you can freely modify scope implementation details as you want. It
just simple function customisable for you purposes.
That's why it is much better than "local <toclose> single_var"

Reply | Threaded
Open this post in threaded view
|

Re: Another option for closing variables

pocomane
On Tue, Jul 16, 2019 at 8:43 AM Sergey Kovalev <[hidden email]> wrote:
> There is no way to extend this attributes only hard coded const and toclose
..
> There is no annotation system. There are two hard coded cases.

Ok, maybe "Annotation system" is not a good name since it can not be
extended. However, it is not just an "Hard coded case". It is a new
syntax that could be used in several part of the language. Only two
cases are handled, just for now. On the list already appeared two
interesting proposal (at least for me):

<static> to handle first time initialization
<error-on-free-name> to protect a block of code from identifier typos
(to be honest, it was proposed to me, and it needs to add annotations
also to do/end block)

> You can not write "local <const> a,b"

With the last commits, you can write, for example:

local <const> a, b = 1, 2

> I prefer to use runtime constants:
...
> > But with the last commits we got constant propagation, so I really want <const>. And if we already have annotatations probably <toclose> is worth too.
> What does it mean exactly?

Constant propagation is exactly what makes compile time constants
better than runtime ones. As Roberto said [1]:

> Local constant variables initialized with compile-time constants are optimized away from the code.

(I did not look at code, however I performed some test, and it seems
to me that constant upvalues are optimized away too).

pocomane

[1] https://github.com/lua/lua/commit/f6aab3ec1f111cd8d968bdcb7ca800e93b819d24

Reply | Threaded
Open this post in threaded view
|

Re: Another option for closing variables

Sergey Kovalev
вт, 16 июл. 2019 г. в 10:46, pocomane <[hidden email]>:
>
> > You can not write "local <const> a,b"
>
> With the last commits, you can write, for example:
>
> local <const> a, b = 1, 2
> https://github.com/lua/lua/commit/f6aab3ec1f111cd8d968bdcb7ca800e93b819d24
I see here only this:
/* stat -> LOCAL ATTRIB NAME {',' ATTRIB NAME} ['=' explist] */
local <const> x, <const> k3_78_4 = 10, k3_78 / 4

So if you intend to define long list for example of jvm opcodes or
some flags you have to write local <const> FLAG1, <const> FLAG2 =
0x100,0x200
And how you will store such constants in object
vm.opcodes={ op1=10, op2=20 } -- ?? local<const>
vm.opcodes.op3=30 -- ?? local<const>

Constant attribute is not real constant type or it's modificator. It
is only compiler annotation for local variable/upvalue.

> Constant propagation is exactly what makes compile time constants
> better than runtime ones. As Roberto said [1]:
> > Local constant variables initialized with compile-time constants are optimized away from the code.
>
> (I did not look at code, however I performed some test, and it seems
> to me that constant upvalues are optimized away too).
This is very good but still there is no way how to pass them into
functions or classes (table with metatables)
This syntax expects only local use of this restriction. If
implementation is efficient but solution is so limited why we need it?

If you want ability to control code synthesis process it should be
done explicitly. Code synthesis and compilation should be separated.
Code transformation and rule checking should be done as script to have
control over code generation before and after compilation stage.
But pushing all into compiler will result only in complexity grow.

Reply | Threaded
Open this post in threaded view
|

Re: Another option for closing variables

Gé Weijers

On Tue, Jul 16, 2019 at 1:43 AM Sergey Kovalev <[hidden email]> wrote:

If you want ability to control code synthesis process it should be
done explicitly. Code synthesis and compilation should be separated.
Code transformation and rule checking should be done as script to have
control over code generation before and after compilation stage.
But pushing all into compiler will result only in complexity grow.


If you want code transformation you need a representation like an AST (abstract syntax tree) or similar to work on, which would add way more complexity than adding simple constant propagation to a one-pass compiler like Lua's compiler. Once you do all that you end up with what's essentially a Lisp implementation under the hood. It'll compile slower, and use more memory.

--

12