lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


On Tue, Sep 14, 2010 at 1:17 PM, Drake Wilson <drake@begriffli.ch> wrote:
> The primary currently-valid plain-Lua form of the above that works
> correctly is:
>
>  -- (declaration if necessary) local value;
>  if a_is_valid then
>    value = do_something_with(a)
>  else
>    value = some_default
>  end
>
> with possible reformatting.  However, this can require the creation of
> extra temporaries instead of being able to embed the whole thing into
> an argument in a function call, for instance.  The target name(s) are
> also duplicated, which could lead to other errors during maintenance.
> So it works and is semantically clean but doesn't compose well.

Those are good points, which I restate as follows.  To me, indicators
of code quality include

  - immutability of variables
  - minimal variable scope
  - minimal repetition

because they reduce the number of possible meanings of code, making it
easier to grasp and analyze.  For example, this code:

  local x = f() or y

is much superior to its statement-like form,

  local temp = f()
  local x
  if temp then x = temp else x = y end

on each of the above three points: x is set once (in fact, in C it can
be declared "const", and in Lua it is highlighted as immutable by
LuaInspect), there is no "temp" variable whose scope extends beyond
its use, and the short expression has immediately recognizable
meaning.

When Lua lacks an expression-like syntax for something, I tend to
resort to writing it in a way that mimics it.  To use the above
example, if the syntax "f() or y" were not available, I might have
instead written it as

  local _temp = f()
  local x; if _temp then
    x = _temp
  else
    x = y
  end

To take a real-world example, you'll find in LuaInspect some code that
defines a variable "argvalues_concrete" with something like this:

  local argvalues_concrete = true; do  -- true iff all function args known
    if #ast >= 2 then
      local firstargvalue; if isinvoke then
        firstargvalue = ast.valueself
      else
        firstargvalue = ast[2].value
      end
      if unknown(firstargvalue) then
        argvalues_concrete = false
      else  -- test remaining args
        for i=3,#ast do
          if unknown(ast[i].value) then
            argvalues_concrete = false
            break
          end
        end
      end
    end
  end

This is pretending to simulate the following expression, if it were
allowed in Lua:

  local argvalues_concrete = do
    if #ast >= 2 then
      local firstargvalue = if isinvoke then
        ast.valueself
      else
        ast[2].value
      end
      if unknown(firstargvalue) then
        false
      else
        for i=3,#ast do
          if unknown(ast[i].value) then
            break false
          end
        end
        true
      end
    else
      true
    end
  end

To explain that syntax above...Block statements are allowed inside
expressions provided the last statement in the block is replaced by an
expression that is to be returned by the block.  This is similar to
Eric Man's suggestion above for `if` statements but generalized to
other statement types.  I also allow `break` to have a return value.
I propose this syntax as an answer to David Kastrup's proposal:

On Tue, Sep 14, 2010 at 7:23 AM, David Kastrup <dak@gnu.org> wrote:
> I think that local variables and assignments do a good job for that in
> an imperative language.  If other people disagree, then one should
> figure out how to smoother wrap the existing control structures into
> expressions rather than make functionally equivalent things that look
> utterly different.

That example I gave above might best be made into a function though,
and as written the translation is trivial.

Here's another example appearing immediately after:

  -- list of values of arguments.
  local argvalues; do
    argvalues = {n=#ast-1}
    for i=1,argvalues.n do
      argvalues[i] = ast[i+1].value
    end
    if isinvoke then argvalues[1] = ast.valueself end
  end