I have finally released LFE, Lisp Flavoured Erlang, which is a lisp syntax front-end to the Erlang compiler. Code produced with it is compatible with "normal" Erlang code. The is an LFE-mode for Emacs and the lfe-mode.el file is include in the distribution. Most things seem to work but some things have not been done yet:
- The interpreter does handle recursive letrecs, binaries, receive or try. - There is no lisp shell. - Documentation!
Yet. The system will be updated as new features are added. This is the 1st release so there is much left to do.
I have include the existing documentation lfe_guide.txt in this mail. There are a number of LFE test files and a version of the LFE interpreter written in LFE as examples of code. There are also a number of issues which need to be decided for the next version and I have included a file lfe_issues.txt which describe them in this mail. Both files are in the distribution.
Note that while this this lisp has been inspired by Scheme (one of the issues) it is a NOT Scheme, it is Erlang with a lisp syntax and many nice lisp features. Not for that matter is it Common Lisp. In fact features of the Erlang engine mean that it is actually impossible to implement full Scheme of CL. No, they shouldn't be changed or added.
It was quite writing Erlang code in lisp and I could easily consider using a lisp syntax for Erlang. I suppose it depends where your preferences lye. It was also easy to get into the traditional lisp habit of using long names for everything which I personally think is not a Good Thing. Perhaps we should do AFE, Arc Flavoured Erlang, instead? Although I think they have gone too far and missed what makes programs easy to read.
Macros are very nice, and it is easy to generate LFE code, which is one of the benefits of lisp syntax.
LFE also shows that it would probably be possible to write other front-ends inspired by other languages, though why anyone should want to I don't know. Perhaps back to a real Prolog syntax again.
Robert Virding wrote: > Function bindings > -----------------
> Core Erlang (and Erlang and the BEAM) separate variable and function > bindings. A function binding has a name and an arity, this is a result > of allowing different functions to have the same name but different > number of arguments. Also there is no concept of a function reference > built into the Core and BEAM. It is therefore impossible to get a > reference to a function and pass it around, creating a fun which calls > this function is not the same thing. So code like:
> In CL: (funcall #'cons a b) > In Scheme: (let ((my-cons cons)) (my-cons a b))
> CANNOT work in Erlang.
I think you can relax, Robert. This is not a problem.
In plain Erlang, you'd write
MyCons = fun cons/2, MyCons(A,B)
In Core Erlang, you write it like this:
let MyCons = 'cons'/2 in apply MyCons(A, B)
Neither of these things mean that you'll necessarily get another closure that will delegate the call - that's an old implementation detail, which is not universally valid anymore. (E.g., with inlining enabled, this will reduce to a direct call "cons(A, B)".) And in particular, there is just a single variable namespace.
You're right that Core Erlang has no special notation ("fun f/N") for referring to a function (as opposed to defining it) - that's because the keyword "fun" is not needed. Core Erlang has two flavours of variables: normal Erlang-style variables, and 'atom'/integer function names. (We could actually have used only the first form, but then the Core Erlang code would be harder to understand, and the export section would have to look something like "export [ F17 as 'foo'/2, F32 as 'bar'/1, ... ]".) You can only define function-name variables in letrec-expressions, not in let-expressions, but this is no limitation in general, since you can easily bind a plain variable to a function-name variable, as above.
The main point here is: when you see some expression "'f'/3" in Core Erlang or "fun f/3" in Erlang, think of it as just a name, a variable. Forget about how "fun f/3" was originally implemented. This is one of the big things about Core Erlang: except for when you need to create names of exported functions, that will be visible outside the module, a variable is just a reference to some value, functional or not, and it does not matter whether it was defined in a letrec or a let. This makes it much easier to do transformations like inlining on the Core language level.
/Richard
PS. I know that it's not easy to grok the full consequences of the Core Erlang specification; one has to sort of clear one's mind and start from scratch - if the spec doesn't prohibit something, then it should just work (assuming that the compiler doesn't have a bug or is incomplete).
On Sun, Mar 2, 2008 at 9:15 AM, Richard Carlsson <richa...@it.uu.se> wrote: > Robert Virding wrote: > > Function bindings > > -----------------
> > Core Erlang (and Erlang and the BEAM) separate variable and function > > bindings. A function binding has a name and an arity, this is a result > > of allowing different functions to have the same name but different > > number of arguments. Also there is no concept of a function reference > > built into the Core and BEAM. It is therefore impossible to get a > > reference to a function and pass it around, creating a fun which calls > > this function is not the same thing. So code like:
> > In CL: (funcall #'cons a b) > > In Scheme: (let ((my-cons cons)) (my-cons a b))
> > CANNOT work in Erlang.
> I think you can relax, Robert. This is not a problem.
> In plain Erlang, you'd write
> MyCons = fun cons/2, > MyCons(A,B)
> In Core Erlang, you write it like this:
> let MyCons = 'cons'/2 in apply MyCons(A, B)
> Neither of these things mean that you'll necessarily get another > closure that will delegate the call - that's an old implementation > detail, which is not universally valid anymore. (E.g., with inlining > enabled, this will reduce to a direct call "cons(A, B)".) And in > particular, there is just a single variable namespace.
> You're right that Core Erlang has no special notation ("fun f/N") for > referring to a function (as opposed to defining it) - that's because the > keyword "fun" is not needed. Core Erlang has two flavours of variables: > normal Erlang-style variables, and 'atom'/integer function names. (We > could actually have used only the first form, but then the Core Erlang > code would be harder to understand, and the export section would have to > look something like "export [ F17 as 'foo'/2, F32 as 'bar'/1, ... ]".) > You can only define function-name variables in letrec-expressions, not > in let-expressions, but this is no limitation in general, since you can > easily bind a plain variable to a function-name variable, as above.
> The main point here is: when you see some expression "'f'/3" in Core > Erlang or "fun f/3" in Erlang, think of it as just a name, a variable. > Forget about how "fun f/3" was originally implemented. This is one of > the big things about Core Erlang: except for when you need to create > names of exported functions, that will be visible outside the module, > a variable is just a reference to some value, functional or not, and > it does not matter whether it was defined in a letrec or a let. This > makes it much easier to do transformations like inlining on the Core > language level.
> /Richard
> PS. I know that it's not easy to grok the full consequences of the Core > Erlang specification; one has to sort of clear one's mind and start from > scratch - if the spec doesn't prohibit something, then it should just > work (assuming that the compiler doesn't have a bug or is incomplete).
- Of course, the interpreter does *NOT* handle recursive letrecs, binaries, receive or try. Yet. - The token syntax is specified in leex but I have included a copy of a generated erlang file. - The compiler is "sensitive" to some errors and will occasionally crash, though it should only generate correct code. - The line numbers used in error messages are the line of the beginning of the form in which the error occurs. As the forms are truly raw data this was the best way I could get line numbers in.
The answer to Roberto's question about line number 6666 is just I wanted a big number which was guaranteed not to clash with users code. And 666 seemed to small. :-)
- I have been thinking about a way to load a module into the shell (when it exists) and I think I have worked out a way to do it. With a little bit of luck it should also support macros. You might actually be able to save a module from the shell as well but I have not thought this through properly yet.
Please report bugs and other mis-features and I will fix them as soon as I can. Unfortunately I am using a toy computer so sending out patches will be difficult.
Robert
On 02/03/2008, Robert Virding <rvird...@gmail.com> wrote:
> I have finally released LFE, Lisp Flavoured Erlang, which is a lisp syntax > front-end to the Erlang compiler. Code produced with it is compatible with > "normal" Erlang code. The is an LFE-mode for Emacs and the lfe-mode.elfile is include in the distribution. Most things seem to work but some > things have not been done yet:
> - The interpreter does handle recursive letrecs, binaries, receive or try.
> - There is no lisp shell. > - Documentation!
> Yet. The system will be updated as new features are added. This is the 1st > release so there is much left to do.
> I have include the existing documentation lfe_guide.txt in this mail. > There are a number of LFE test files and a version of the LFE interpreter > written in LFE as examples of code. There are also a number of issues which > need to be decided for the next version and I have included a file > lfe_issues.txt which describe them in this mail. Both files are in the > distribution.
> Note that while this this lisp has been inspired by Scheme (one of the > issues) it is a NOT Scheme, it is Erlang with a lisp syntax and many nice > lisp features. Not for that matter is it Common Lisp. In fact features of > the Erlang engine mean that it is actually impossible to implement full > Scheme of CL. No, they shouldn't be changed or added.
> It was quite writing Erlang code in lisp and I could easily consider using > a lisp syntax for Erlang. I suppose it depends where your preferences lye. > It was also easy to get into the traditional lisp habit of using long names > for everything which I personally think is not a Good Thing. Perhaps we > should do AFE, Arc Flavoured Erlang, instead? Although I think they have > gone too far and missed what makes programs easy to read.
> Macros are very nice, and it is easy to generate LFE code, which is one of > the benefits of lisp syntax.
> LFE also shows that it would probably be possible to write other > front-ends inspired by other languages, though why anyone should want to I > don't know. Perhaps back to a real Prolog syntax again.
On 02/03/2008, Tim Fletcher <twog...@gmail.com> wrote:
> > I have finally released LFE, Lisp Flavoured Erlang, which is a lisp > syntax > > front-end to the Erlang compiler.
> Does it require a particular version of Erlang?
It was developed on R12B-0 but I don't think there should be any trouble on running it on R11B. I don't think that Core has changed very much, at least at the level I am using it. Let me know if you detect anything strange.
> Please report bugs and other mis-features and I will fix them as soon as > I can.
Well, since you asked for it, at a click of a button dialyzer reports the following:
lfe_scan.erl:215: The pattern <Rest, Line, {'end_token', T}, Ts> can never match the type <_,_,'error' | 'skip_token' | {'error',[32 | 97 | 99 | 101 | 103 | 104 | 105 | 108 | 114 | 116,...]},_> lfe_scan.erl:164: The pattern <Rest, Line, {'end_token', T}> can never match the type <_,_,'error' | 'skip_token' | {'error',[32 | 97 | 99 | 101 | 103 | 104 | 105 | 108 | 114 | 116,...]}> lfe_scan.erl:118: The pattern <Rest, Line, {'end_token', T}, Ts> can never match the type <_,_,'error' | 'skip_token' | {'error',[32 | 97 | 99 | 101 | 103 | 104 | 105 | 108 | 114 | 116,...]},[{atom(),_} | {'number',_,_} | {'string',_,[any()]} | {'symbol',_,atom()}]> lfe_scan.erl:255: The pattern <Rest, Line, {'end_token', _T}, Error> can never match the type <_,_,'error' | 'skip_token' | {'error',[32 | 97 | 99 | 101 | 103 | 104 | 105 | 108 | 114 | 116,...]},_>
lfe_macro.erl:490: The pattern {'yes', _} can never match the type 'error' | {'ok',_} lfe_macro.erl:489: The pattern {'yes', _} can never match the type 'error' | {'ok',_}
The first set is just dead clauses. The second set, is a mismatch of the return value of a function.
Dialyzer also reports some more additional warnings which are also reported by the BEAM compiler, so I am suppressing these.
> > Please report bugs and other mis-features and I will fix them as soon as > > I can.
> Well, since you asked for it, at a click of a button dialyzer reports > the following:
> lfe_scan.erl:215: The pattern <Rest, Line, {'end_token', T}, Ts> can > never match the type <_,_,'error' | 'skip_token' | {'error',[32 | 97 | > 99 | 101 | 103 | 104 | 105 | 108 | 114 | 116,...]},_> > lfe_scan.erl:164: The pattern <Rest, Line, {'end_token', T}> can never > match the type <_,_,'error' | 'skip_token' | {'error',[32 | 97 | 99 | > 101 | 103 | 104 | 105 | 108 | 114 | 116,...]}> > lfe_scan.erl:118: The pattern <Rest, Line, {'end_token', T}, Ts> can > never match the type <_,_,'error' | 'skip_token' | {'error',[32 | 97 | > 99 | 101 | 103 | 104 | 105 | 108 | 114 | 116,...]},[{atom(),_} | > {'number',_,_} | {'string',_,[any()]} | {'symbol',_,atom()}]> > lfe_scan.erl:255: The pattern <Rest, Line, {'end_token', _T}, Error> can > never match the type <_,_,'error' | 'skip_token' | {'error',[32 | 97 | > 99 | 101 | 103 | 104 | 105 | 108 | 114 | 116,...]},_>
> lfe_macro.erl:490: The pattern {'yes', _} can never match the type > 'error' | {'ok',_} > lfe_macro.erl:489: The pattern {'yes', _} can never match the type > 'error' | {'ok',_}
> The first set is just dead clauses. The second set, is a mismatch of the > return value of a function.
The first lot from lfe_scan are just handling a possible legal case which can be used in leex but which I don't use here. You can return a token {end_token,Token} which means that if you use the tokens/2/3 call then it will read tokens up to and including an {end_token,...}. Perfect for Erlang '. '.
The second one was a bug, now fixed, thank you.
Dialyzer also reports some more additional warnings which are also
> reported by the BEAM compiler, so I am suppressing these.
None of the compiler warnings are interesting so I ignore them.
Robert, this is very cool. I already started playing with LFE and it's great. Thanks a lot for sharing it.
I have a couple of suggestions:
- it would be nice to have a preprocessor/parse transform that converts the form
(foo:bar baz ..)
to
(: foo bar baz ...)
I find the former much more readable.
- It would like to have an Erlang-style '=' operator for binding instead of (let). The main difference is in the number of parentheses and the scoping rules. So, instead of
(let ((a 1) (b 2)) (+ a b))
you could write
(= (a 1) (b 2)) (+ a b)
- String literals should be interpreted as literal lists rather than sexps. It seems redundant to write a single quote before a string literal.
- It would be great to have syntax for multi-line strings and binaries.
- Distributing LFE under a standard open source license would encourage its adoption and foster community contributions. lfe_scan.erl has this license notice:
%%% (C)Robert Virding. This stuff is mine, distributed without %%% warranty "as is" and may not be used commercially without letting %%% me know.
This wording is ambiguous. Is the only requirement to using LFE commercially to let you know, or it it required to buy a license for commercial use? What happens to user contributions? Etc.
> I have finally released LFE, Lisp Flavoured Erlang, which is a lisp syntax > front-end to the Erlang compiler. Code produced with it is compatible with > "normal" Erlang code. The is an LFE-mode for Emacs and the lfe-mode.el file > is include in the distribution. Most things seem to work but some things > have not been done yet:
> - The interpreter does handle recursive letrecs, binaries, receive or try. > - There is no lisp shell. > - Documentation!
> Yet. The system will be updated as new features are added. This is the 1st > release so there is much left to do.
> I have include the existing documentation lfe_guide.txt in this mail. There > are a number of LFE test files and a version of the LFE interpreter written > in LFE as examples of code. There are also a number of issues which need to > be decided for the next version and I have included a file lfe_issues.txt > which describe them in this mail. Both files are in the distribution.
> Note that while this this lisp has been inspired by Scheme (one of the > issues) it is a NOT Scheme, it is Erlang with a lisp syntax and many nice > lisp features. Not for that matter is it Common Lisp. In fact features of > the Erlang engine mean that it is actually impossible to implement full > Scheme of CL. No, they shouldn't be changed or added.
> It was quite writing Erlang code in lisp and I could easily consider using a > lisp syntax for Erlang. I suppose it depends where your preferences lye. It > was also easy to get into the traditional lisp habit of using long names for > everything which I personally think is not a Good Thing. Perhaps we should > do AFE, Arc Flavoured Erlang, instead? Although I think they have gone too > far and missed what makes programs easy to read.
> Macros are very nice, and it is easy to generate LFE code, which is one of > the benefits of lisp syntax.
> LFE also shows that it would probably be possible to write other front-ends > inspired by other languages, though why anyone should want to I don't know. > Perhaps back to a real Prolog syntax again.