[erlang-questions] Luerl - Lua in Erlang: Interface Definition

Henning Diedrich hd2010@REDACTED
Sat Feb 25 07:46:38 CET 2012


This may come close:

https://github.com/Eonblast/luerl/tree/henning

For the criticism I still have, see inline comments please.


On 2/24/12 12:19 AM, Robert Virding wrote:
> Hi Henning,
>
> Ok we will go towards making them more Lua-C like. But we are running 
> in an Erlang environment so they will handle errors in an Erlangy way:
>
> - The 'eval' functions will catch errors and return {ok,Result} | 
> {error,Reason}.
What this gives away is the chance of less cluttered code, to use eval 
'in-line' with no pattern matching result extraction, if so wanted.
> - The 'do' and 'call' functions will not catch errors and return 
> {Result,NewState}. Errors caught in the Lua code, with 'pcall' which 
> does not exist yet, will of course not generate an error at the Erlang 
> interface.

Isn't the chance to look at the soon to be discarded stack something 
that needs the support of giving over a suitable callback. And thus 
offer pcall from Erlang?

> - We will initialise/cleanup the state with 'start'/'stop' in an 
> Erlangy way.
> - We will 'encode'/'decode' Lua data.
> - And we will both 'load' and 'compile' Lua and load/compile(String) 
> -> {ok,Form} | {error,Reason}.
I did all those changes, and see /examples/hello/hello2.erl. The 
interface comes across as bloated like this. I think.

Best,
Henning

> The erlang format of errors from the luerl runtime will be something 
> {luerl_error,Error} where will be something like {badarg,sub,Args}. I 
> will try to give them similar names as in Lua. Erlang errors are very 
> seldom string values, in some cases there are format_error/1 functions 
> which return string values. This will also make it easy to see which 
> are Lua errors and which are from the Erlang code.
>
> Robert
>
> ------------------------------------------------------------------------
>
>     Hi Robert,
>
>     Thanks for the feedback. Yes, it's a matter of viewpoint, from
>     Erlang or Lua.
>
>     If you feel the audience for Luerl would be people who did work
>     with Lua before, many of them would know the C interface and thus,
>     it could be helpful for them to stick to these names.
>
>     Since I work with Lua, embedded Lua and Erlang, I would treasure
>     consistency between the C and Erlang underside of Lua.
>
>     Lua is so much for embedding that the C interface functions are
>     actually listed in the manual earlier than the language functions
>     (or at least once it was so).
>
>     I'll agree that 'load' is not intuitively right. But it would
>     increase consistency.
>
>     'dofile' is a staple of Lua proper, also exists in the C interface
>     and again would probably be what somebody coming from Lua would
>     look for, so would be --- nice to have in my view. 'evalfile'
>     exists for symmetry.
>
>     As I wrote, my interface proposal is bloated, but the differences
>     between 'do' and 'call' would be that 'call' only takes
>     pre-compiled chunks and crashes with anything else. While "do" is
>     more careless and can be fed anything: Lua code in Strings,
>     Binaries, or pre-compiled chunks.
>
>     "eval" - for easiest use, in place, returns pure Result
>     "do" - same as eval but returns Result and State*
>     "call" - takes only precompiled chunks, returns Result and State
>
>     * (in Erlang, you can't just /not/ fetch the second return value,
>     as is possible in Lua)
>
>     My two cents on: "encode" and "decode" I find them better but
>     still not perfect. It's really again the perspective from Erlang.
>     "wrap"/ "unwrap"? "stage"/"unstage"? (Looks like no difference to
>     "encode" at first sight -- but I do think there is one.)
>
>     As for error handling: I came to think that a try-catch wrap in
>     eval as standard service would be nice. So using eval you'd be
>     sure it will always return, of with an error message. While that
>     may in fact be too much Lua thinking, and too alien to Erlang,
>     it's what comes to mind for how I would like "eval" to be a
>     carefree, fast thing to use. (Erlang has a "let it crash"
>     philosophy where errors are usually handled but a process
>     automatically restarted.)
>
>     The whole point of "pcall" is to be a protected version of "call".
>     Using "pcall" in Lua/C interface one can be sure that errors don't
>     crash the program and can be handled. Specifically by a handler
>     function which has access to the state before the stack is discarded.
>
>     I think it may make sense to treat errors that originate in Lua
>     (or even in the Luerl VM itself, while in development) differently
>     from errors that originate in the Erlang source.
>
>     Best,
>     Henning
>
>     -- 
>     *Henning Diedrich*
>     CEO
>
>
>     	
>     	Eonblast Corporation
>     hdiedrich@REDACTED
>     +1.404.418.5002 w
>     www.eonblast.com
>
>
>     This email contains confidential and/or privileged information. If
>     you are not the intended recipient (or have received this email in
>     error) please notify the sender immediately and destroy this
>     email. Any unauthorized copying, disclosure or distribution of the
>     material in this email is prohibited.
>
>
>     On 2/22/12 3:04 AM, Robert Virding wrote:
>
>         Hi,
>
>         Most of the difference is just in terminology and from which
>         side you view it, from erlang or Lua. For example, for me
>         loading means actually getting it into the sytem not just
>         compiling it into a runnable form. And I view these functions
>         from the erlang side. I will also admit that I am a bit hazy
>         as to how the C-side functions work, it seems easier within
>         the language.
>
>         'eval' and 'do' work well for me, though I do wonder if we
>         need 'evalfile' and 'dofile'. I prefer 'compile' instead of
>         'load' as this is what is does. 'call' is fine. What is the
>         difference between 'call' and 'do'? For creating and
>         destroying state I would prefer either 'new' / 'delete' or
>         'start' / 'stop' depending on whether you want to view the
>         state as just data or as some form of concurrent activity, a
>         separate process in Erlang.
>
>         I would use 'encode' / 'decode' for converting between an
>         Erlang representation and an internal luerl representation. So:
>
>         luerl:encode(Erlang, State) -> {Luerl,State}.
>         luerl:decode(Luerl, State) -> Erlang.
>
>         Encoding will modify the state as tables only exist within
>         that state. If it not done this way then there is no need of
>         these functions as the representation is the same and this
>         conversion will be done in the other interface functions. So
>         for example a Lua strings are Erlang binaries and tables will
>         are a list of tuple pairs,
>
>         Lua {['a']=97,['b']=98,10,20,30} <==> Erlang
>         [{1.0,10.0},{2.0,20.0},{3.0,30.0},{<<"a">>,97.0},{<<"b">>,98.0}]
>
>         Putting Lua tables into the state is also the only way to
>         handle the difference in the meaning of equality between Lua
>         and Erlang.
>
>         For errors I have been thinking in the Erlang way. The only
>         function to actually return an error value would be 'compile'
>         (or 'load') which would become
>
>         luerl:compile(String) -> {ok,Chunk} | {error,Reason}.
>
>         All errors during the Lua execution, unless they are caught
>         internally, would result in Erlang exceptions which would
>         handled in the normal Erlang way, either by catching it or
>         having the Erlang process die.
>
>         The result returned from Lua and the arguments to a call would
>         be a list of Erlang values. If no values are returned the list
>         is empty otherwise it would contain same number of elements as
>         in the 'return' statement.
>
>         Those are my thoughts for the moment. Now I will go back and
>         try and code the idiosyncrasies of string.sub.
>
>         Robert
>
>         ------------------------------------------------------------------------
>
>             Hi Robert,
>
>             To allow for String|Chunk, the chunks returned from ps/1
>             would have to be wrapped to be distinguishable from the
>             Strings. I think 'functiondef' could be the right choice.
>
>             For the names, I'd propose to maybe stay closer to the Lua
>             language function names and its C interface [1].
>
>             But at any rate, to maybe decide for one of "do" and
>             "eval" to 1) return bare results 2) return {Result,
>             State}. Rather than making this dependent on whether State
>             was handed in or not as a parameter?
>
>             Since Lua uses dofile() both in the Lua language and the C
>             interface, (and since of course neither case returns
>             state), the "do" functions look earmarked for returning
>             the simple, bare bones Result. However ... somehow "eval"
>             is a better fit for a function that is expected to return
>             something.
>
>             Lua's C interface uses "load" for parsing-only: load,
>             loadfile, lua_load [2], lua_loadfile, lua_loadstring,
>             lua_loadbuffer.
>
>             This could be an alternative to wrapping the chunks: for
>             load, in Lua /"the string mode controls whether the chunk
>             can be text or binary (that is, a precompiled chunk). It
>             may be the string "b" (only binary chunks), "t" (only text
>             chunks), or "bt" (both binary and text). The default is
>             "bt". /[5]
>
>             The type that the loads return is 'function': /"If there
>             are no syntactic errors, returns the compiled chunk as a
>             function; otherwise, returns nil plus the error message."
>             --- /therefore, the right chunk wrapper could be {
>             functiondef, ... }, instead of compiled chunks being lists
>             as outermost type.
>
>             Execution of pre-parsed/compiled chunks is "call": pcall,
>             xpcall, lua_call, lua_pcall [3],  and lua_pcallk.
>
>             State is created and destroyed by lua_newstate and lua_close.
>
>             There is no "eval" in Lua.
>
>             So here's my proposal:
>
>             luerl:eval(String|Chunk[, State]) -> Result.
>             luerl:evalfile(PathString[, State]) -> Result.
>
>             luerl:do(String|Chunk[, State]) -> {Result, NewState}.
>             luerl:dofile(PathString[, State]) -> {Result, NewState}.
>
>             luerl:newstate() -> State.
>             luerl:close(State) -> ok.
>
>             luerl:load(String) -> {ok, Chunk}.
>             luerl:loadfile(PathString) -> {ok, Chunk}.
>             luerl:call(Chunk[, State][, ErlParamList()]) -> {Result,
>             NewState}.
>
>             luerl:tolua(list()) -> LuerlTermsList().
>             luerl:toerlang(LuerlTermsList()) -> list().
>
>             This would be somewhat in keeping with Lua's naming.
>
>             I am unclear about error state returns. Simply in the
>             Result I guess?
>
>             Relative to your proposal that is:
>             luerl:eval(String|Chunk) -> Result. =>
>             luerl:eval(String|Chunk[, State]) -> Result.
>             luerl:dofile(String) -> Result. =>
>             luerl:dofile(PathString[, State]) -> {Result,State}.
>             luerl:new() -> State. (currently luerl:init() -> State.)
>             =>luerl:newstate() -> State.
>             luerl:eval(String|Chunk, State) -> {Result,NewState}. =>
>             luerl:eval(String|Chunk, State) -> {Result,NewState}.
>             luerl:dofile(String, State) -> {Result,NewState}. => same
>             luerl:compile(String) -> {ok,Chunk}. => luerl:load(String)
>             -> {ok,Chunk}.
>
>             Beyond that, I had thought with 'interface' you would be
>             addressing the direct interchange of values between Erlang
>             and Lua. I'd be all for making the collection of tables in
>             the Lua state accessible and writable, directly, somehow
>             navigating into it using a key structure. And if possible,
>             vice versa: giving Lua direct access to Erlang state.
>
>             Best,
>             Henning
>
>
>             [1] http://www.lua.org/manual/5.2/manual.html#4.8
>
>             [2] /One note I like, in the description of the C function
>             lua_load : "The source argument gives a name to the chunk,
>             which is used for error messages and in debug information
>             (see §4.9)."
>             /http://www.lua.org/manual/5.2/manual.html#lua_load -
>             http://www.lua.org/manual/5.2/manual.html#2.3
>
>             [3] /When you use xpcall or lua_pcall, you may give a
>             message handler to be called in case of errors. This
>             function is called with the original error message and
>             returns a new error message. It is called before the error
>             unwinds the stack, so that it can gather more information
>             about the error, for instance by inspecting the stack and
>             creating a stack traceback. This message handler is still
>             protected by the protected call; so, an error inside the
>             message handler will call the message handler again. If
>             this loop goes on, Lua breaks it and returns an
>             appropriate message./ -
>             http://www.lua.org/manual/5.2/manual.html#2.3
>
>             [4] In Lua (not the C interface), dofile does not run in
>             protected mode. http://www.lua.org/manual/5.2/manual.html#6.1
>
>             [5] http://www.lua.org/manual/5.2/manual.html#6.1
>
>
>
>             On 2/20/12 10:59 PM, Robert Virding wrote:
>
>                 [snip] I had planned something along the lines of:
>
>                 luerl:eval(String|Chunk) -> Result.
>                 luerl:dofile(String) -> Result.
>
>                 Basic simple interface which initialises a state and
>                 evaluates the chunk String in it returning a list of
>                 return values (if any). For example luer:eval("local
>                 t={'a','b'} return t[1],t[2]") will return
>                 [<<"a">>,<<"b">>]. luerl:dofile/1 is not really necessary.
>
>                 luerl:new() -> State.
>                 luerl:eval(String|Chunk, State) -> {Result,NewState}.
>                 luerl:dofile(String, State) -> {Result,NewState}.
>                 luerl:compile(String) -> {ok,Chunk}.
>
>                 A more complex interface. luerl:new/0 creates an
>                 initial state. luerl:eval/2 will evaluate a chunk in a
>                 state and return the values and the updated state.
>                 This state can be reused to evaluate new chunks. Again
>                 luerl:dofile/2 is not really necessary.
>                 luerl:compile(String) compiles the string into an
>                 internal form ready to run in eval/1/2.
>
>                 Result is always a list of return values which may be
>                 empty if the chunk does not do a return with values.
>                 For data types:
>
>                 Lua strings are binaries
>                 Lua numbers are floats
>                 Lua tables are orddicts (property lists) of key-value
>                 tuples
>                 Lua true, false, nil are just the atoms true, false, nil
>
>                 Anyway something along those lines. It might be nice
>                 to have a function call wrapper which would allow you
>                 a more erlang like way of calling a luerl function.
>
>                 Robert
>
>                 ------------------------------------------------------------------------
>
>                     Regarding interface function names:
>
>                     I wonder what logic Luerl's names of do and eval
>                     follow:
>
>                     dofile/1, like eval/1, returns a pragmatic Ret
>
>                     while do/2 returns {String, State}
>
>                     Since you are exporting ps/1, there should maybe
>                     be a dochunk/2?
>
>                     And /1, too?
>
>                     Or should it maybe be evalchunk/1, dochunk/2 (the
>                     /2s with State as second parameter)?
>
>                     Here are some relevant functions from Lua's C
>                     interface.
>
>                         *luaL_dofile*
>
>                         [-0, +?, m]
>                         int luaL_dofile (lua_State *L, const char
>                         *filename);
>                         Loads and runs the given file. It is defined
>                         as the following macro:
>
>                              (luaL_loadfile(L, filename) ||
>                         lua_pcall(L, 0, LUA_MULTRET, 0))
>                         It returns false if there are no errors or
>                         true in case of errors.
>
>                         *luaL_dostring*
>
>                         [-0, +?, –]
>                         int luaL_dostring (lua_State *L, const char *str);
>                         Loads and runs the given string. It is defined
>                         as the following macro:
>
>                              (luaL_loadstring(L, str) || lua_pcall(L,
>                         0, LUA_MULTRET, 0))
>                         It returns false if there are no errors or
>                         true in case of errors.
>
>                         *luaL_loadstring*
>
>                         [-0, +1, –]
>                         int luaL_loadstring (lua_State *L, const char *s);
>                         Loads a string as a Lua chunk. This function
>                         uses lua_load to load the chunk in the
>                         zero-terminated string s.
>
>                         This function returns the same results as
>                         lua_load.
>
>                         Also as lua_load, this function only loads the
>                         chunk; it does not run it.
>
>                         *luaL_newstate*
>
>                         [-0, +0, –]
>                         lua_State *luaL_newstate (void);
>                         Creates a new Lua state. It calls lua_newstate
>                         with an allocator based on the standard C
>                         realloc function and then sets a panic
>                         function (see §4.6) that prints an error
>                         message to the standard error output in case
>                         of fatal errors.
>
>                         Returns the new state, or NULL if there is a
>                         memory allocation error.
>
>
>                     Source: http://www.lua.org/manual/5.2/manual.html
>
>
>
>
>             _______________________________________________
>             erlang-questions mailing list
>             erlang-questions@REDACTED
>             http://erlang.org/mailman/listinfo/erlang-questions
>
>
>
>     _______________________________________________
>     erlang-questions mailing list
>     erlang-questions@REDACTED
>     http://erlang.org/mailman/listinfo/erlang-questions
>
>

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20120225/b7f1bad5/attachment.htm>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: image/png
Size: 4856 bytes
Desc: not available
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20120225/b7f1bad5/attachment.png>


More information about the erlang-questions mailing list