New feature for lua

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

New feature for lua

Sergey Kovalev
Let inroduce new reserved word "pure_function" into lua.
It should work similar to usual function except
all variables used inside are implicitly local
no access to any upvalues or global namespace _G
So it could interact with arguments it was passed.

This allows to isolate parts of code from whole program.

In lua 5.3.4 there is no way to do it.

Here is example of isolation:

a=1
local b=1

function sandbox(_ENV)
  a=2
  b=2
end

sandbox{}

print("a=",a) -- will print 1
print("b=",b) -- will print 2

Changing environmet could not solve the problem, because of upvalues.
The behaviour depends on current scope. And the scope can be very complex.
This program keep global variable a, but modify b. Variable b is upvalue for sandbox function. If I define a local and b global result will change.

It would be perfect to have ability to isolate part of code from the rest.
It could be used for debugging, plugins and modules in lua.
It could look like:

pure_function sandbox(...)
  a=2
  b=2
end

So the behaviour will be same despite outside code. Even is a and b was declarate somethere upper.

Reply | Threaded
Open this post in threaded view
|

Re: New feature for lua

Egor Skriptunoff-2
On Fri, Apr 5, 2019 at 9:12 AM Sergey Kovalev wrote:
Let inroduce new reserved word "pure_function" into lua.
It should work similar to usual function except
all variables used inside are implicitly local
no access to any upvalues or global namespace _G
So it could interact with arguments it was passed.

This allows to isolate parts of code from whole program.

In lua 5.3.4 there is no way to do it.


There is a way.

function pure_function(source_text)
  return assert(load("local _ENV = {}; return "..source_text))()
end

Usage example:

a=1
local b=1

local sandbox = pure_function[[
function (x)
  a=2
  b=2
  return x^2
end
]]

print(sandbox(5)) - will print 25

print("a=",a) -- will print 1
print("b=",b) -- will print 1


Reply | Threaded
Open this post in threaded view
|

Re: New feature for lua

steve donovan
In reply to this post by Sergey Kovalev
On Fri, Apr 5, 2019 at 8:12 AM Sergey Kovalev <[hidden email]> wrote:
>
> Let inroduce new reserved word "pure_function" into lua.
> It should work similar to usual function except
> all variables used inside are implicitly local
> no access to any upvalues or global namespace _G
> So it could interact with arguments it was passed.

The 'implicitly local' bit will be a surprise to people, I think.
Global-as-default has mostly served us well.

I'm wondering if this could not be done just as a check using bytecode
- i.e. annotate a function as 'pure' in some way and look at the
bytecode to see if it has any upvalues or globals referenced.

Reply | Threaded
Open this post in threaded view
|

Re: New feature for lua

Roberto Ierusalimschy
In reply to this post by Sergey Kovalev
> It should work similar to usual function except
> all variables used inside are implicitly local
> no access to any upvalues or global namespace _G
> So it could interact with arguments it was passed.
>
> This allows to isolate parts of code from whole program.
>
> In lua 5.3.4 there is no way to do it.
>
> [...]

I don't see why one needs this, and how it is related to sandboxing.

A function can only access an upvalue if it is physically written inside
its scope. Any code coming from outside cannot access any upvalue in
your code. It looks like you want to protect your code from itself. As
already said, if you don't want to access an upvalue in your own code,
just don't do it. (For globals, _ENV seems to solve the issue.)

Anyway, if your code is so convoluted that you don't know what is what,
just write that function as the first thing in its chunk, before any
local declaration:

    -- first line in your chunk
    do
      local _ENV = nil
      function aSoCalledPureFunction ()
        ...
      end
    end

-- Roberto

Reply | Threaded
Open this post in threaded view
|

Re: New feature for lua

Sean Conner
In reply to this post by Sergey Kovalev
It was thus said that the Great Sergey Kovalev once stated:
> Let inroduce new reserved word "pure_function" into lua.
> It should work similar to usual function except
> all variables used inside are implicitly local
> no access to any upvalues or global namespace _G
> So it could interact with arguments it was passed.

  So having read the rest of the messages in this thread, I think I
following what you are trying to do.  So instead of the following:

function fromjulianday(jd)
  local a = jd + 32044
  local b = (4 * a + 3) // 146097
  local c = a - (b * 146097) // 4
  local d = (4 * c + 3) // 1461
  local e = c - (1461 * d) // 4
  local m = (5 * e + 2) // 153
 
  return {
    day   = e - (153 * m + 2) // 5 + 1,
    month = m + 3 - 12 * (m // 10),
    year  = b * 100 + d - 4800 + m // 10
  }
end

You could instead write this as:

pure_function fromjulianday(jd)
  a = jd + 32044
  b = (4 * a + 3) // 146097      
  c = a - (b * 146097) // 4      
  d = (4 * c + 3) // 1461      
  e = c - (1461 * d) // 4      
  m = (5 * e + 2) // 153      
     
  return {
    day   = e - (153 * m + 2) // 5 + 1,      
    month = m + 3 - 12 * (m // 10),      
    year  = b * 100 + d - 4800 + m // 10      
  }
end

Or would it have to be?

pure_function fromjulianday(_ENV,jd) -- I don't use _ENV
  a = jd + 32044
  b = (4 * a + 3) // 146097            
  c = a - (b * 146097) // 4            
  d = (4 * c + 3) // 1461            
  e = c - (1461 * d) // 4            
  m = (5 * e + 2) // 153            
     
  return {
    day   = e - (153 * m + 2) // 5 + 1,      
    month = m + 3 - 12 * (m // 10),      
    year  = b * 100 + d - 4800 + m // 10      
  }
end  

  Is this what you are proposing?
 
  To me, a "pure" function is one that only relies upon the parameters given
to it, it has no access to any data outside itself.  So for Lua, no globals
(or _ENV[]) or upvalues.  It a compiled langauge like Haskell such pure
functions can be replaced at compile time in certain circumstances with its
result.  For example:

        x = sin(math.pi)

at compile time x could be replaced with 0 with math.pi is a constant. [1]

  -spc

[1] There's more to this optimization than what I've given here.
        Languages that support this type of optimization can track the
        contents of variables and find constants as parameters even if used
        through a variable, for example:

                pure function foo(x)
                  a = 5
                  b = 6
                  x = x * a + math.sin(b)
                  return x
                end
               
        The compiler can determine that a and b are not modified after
        assignment, so the function can be reduced to:
       
                -- replace variables with constants
                pure function foo(x)
                  x = x * 5 + math.sin(6)
                  return x
                end
               
                -- substitute results of pure functions
                pure function foo(x)
                  x = x * 5 + -0.27941549819893
                  return x
                end
               
                -- remove reassignment of x
                pure function foo(x)
                  return x * 5 - 0.27941549819893
                end
               
        Furthermore, if foo() is ever called with a constant, it too could
        be called at compile time, since it's marked as "pure" so it only
        depends upon values given to it.