lua-users home
lua-l archive

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


> Luiz Henrique de Figueiredo:
>> You're right about "return". The limitation on "break" is because we
>> wanted to leave the possibility of adding break labels in the future
>> (although I don't think we will).

That's a shame, sort of

> It's probably all for the good to keep the Return & Break limitations
> consistent with each other even if it's not actually needed
syntactically.

This has always puzzled me. Under what circumstances would you want to
put a statement after return or break? It could not possibly be executed,
and I'm quite happy to have the syntax checker tell me that.

While we're dreaming, I'll put in my plug again for non-local exits,
Dylan-style, instead of break labels and exceptions. The idea is
very simple:

begin name chunk end

is an expression (not a statement) in which "name" is locally defined as
a first-class continuation with the restriction that it cannot be used
after the begin block is exited. However, it can be passed as an argument.
That is, name is defined as a function with a variable number of arguments;
calling it causes the begin block to evaluate to those arguments.

You can implement both break and return with this mechanism, not that I
am suggesting that but it is an interesting observation.

The implementation should be fairly straightforward in Lua 5; restricted
continuations do not require complex stack manipulation and I would have
thought that a mechanism similar to the one used to heapify upvalues
would also serve to nullify continuations on block exit. I think this is
consistent with the Lua philosophy: provide a simple powerful
infrastructure and let people use it.

Simple example, just for illustration:

Say we have:

function tree_walk(t, condition, dothis)
  if t then
    tree_walk(t.left, condition, dothis)
    if condition(t) then dothis(t) end
    tree_walk(t.right, condition, dothis)
  end
end

function is_variable(t)
  return t.type == "variable"
end

Now, obviously we can say:

function print_all_variables(t)
  tree_walk(t, is_variable, print_node)
end

But the non-local exit also gives a nice solution to this:

function find_leftmost_variable(t)
  return begin gotit
           tree_walk(t, is_variable, gotit)
         end
end

Clearly this could also implement exceptions. But it is significantly more
powerful
than exceptions because it provides for an "exception handler" that doesn't
pop
the stack. Sometimes, for example, you can correct for an exceptional
condition,
and you just want to continue. Dylan handles this by binding exception
conditions
with functions; the function is called when the exception happens. If the
exception
can handle it, it could accept a return value and retry the computation;
however,
it is also possible to bind the condition to a non-local exit and achieve
C++/Java
style exceptions.

Pseudo example with pseudo syntax:

begin exit

  local function giveup(filename)
    log_error(filename .. " was not found, Cannot continue\n")
    exit("Failed miserably")
  end

  local function prompt_for_filename(filename)
    io.write()
    new_filename = gui.file_dialog(filename ..
         "was not found, which file should I use?")
    return new_filename or giveup(filename)
  end

  -- this is the key line, like a try/catch but different

  with file_not_found_error = (interactive and prompt_for_filename) or
giveup do
    ....
    input = io.open(fn)
    ...
  end
  exit("OK")

end

-- io.open pseudo-code:

function io.open(filename)
  while "we haven't succeeded" do
    local file = system.open(filename)
    if file then return file end
    if system.error == ENOENT then
      filename = file_not_found_error(filename)
    -- more error tests
    end
  end
end

Side note: the above requires dynamically local variables; that is, the
with statement does not create a new local called file_not_found_error;
rather, it saves the old value of the global and temporarily resets it to
the value of the expression. On exit, it has to restored the saved value,
even with a non-local exit. I don't think that is too difficult to
implement either, but it does add a bit of bulk.