Lisp Flavoured Erlang ===================== Special syntactic rules ----------------------- #(e e ... ) - Tuple constants #b(e e ... ) - Binary constant, e ... are bytes [ ... ] - Allowed as alternative to ( ... ) Supported Core forms -------------------- (quote e) (cons head tail) (list e ... ) (tuple e ... ) (binary seg ... ) where seg is byte or (val integer|float|binary|bitstring (size n) (unit n) big-endian|little-endian|native-endian signed|unsigned) (lambda (arg ...) ...) (match-lambda - Matches clauses ((arg ... ) [(when e)] ...) ... ) (let ((pat [(when e)] e) ...) ... ) (flet ((name lambda|match-lambda) - Only define local functions ... ) ... ) (fletrec ((name lambda|match-lambda) - Only define local functions ... ) ... ) (begin ... ) (if test true-expr [false-expr]) (case e (pat [(when e)] ...) ... )) (receive (pat [(when e)] ... ) ... (after timeout ... )) (catch ... ) (try e [(case ((pat [(when e)] ... ) ... ))] [(catch (((tuple type value ignore) [(when e)] - Must be tuple of length 3 here! ... ) ... )] [(after ... )]) (call mod func arg ... ) - Call to Mod:Func(Arg, ... ) Supported macro forms --------------------- (: mod func arg ... ) => (call 'mod 'func arg ... ) (?) - Receive next message (++ ... ) (let* (...) ... ) - Sequential let's (flet* (...) ... ) - Sequential flet's (cond ... ) - The normal cond (andalso ... ) (orelse ... ) (fun func arity) - fun func/arity (fun mod func arity) - fun mod:func/arity Patterns -------- Written as normal data expressions where symbols are variables and use quote to match explicit values. Binaries and tuples have special syntax. {ok,X} -> (tuple 'ok x) error -> 'error {yes,[X|Xs]} -> (tuple 'yes (x . xs)) <<34,F/float>> -> (binary 34 (f float)) [P|Ps]=All -> (= (p . ps) all) Repeated variables are *NOT* supported in patterns, there is no automatic comparison of values. It must explicitly be done in a guard. In this version there is no checking of this and if multiple variables occurr then it is undefined which vlaue "wins" and is bound to the variable. _ as the "don't care" variable is supported. This means that the symbol _, which is a perfectly valid symbol, can never be bound through pattern matching. Aliases are defined with the (= pattern1 pattern2) pattern. As in Erlang patterns they can be used anywhere in a pattern. *CAVEAT* The lint pass of the compiler checks for aliases and if they are possible to match. If not an error is flagged. This is not the best way. Instead there should be a warning and the offending clause removed, but later passes of the compiler can't handle this yet. Bindings and Scoping -------------------- Have separate variable and function bindings (name and arity), variables bound by lambda, match-lambda and let, function bindings through (top-level) defines, flet and fletrec. Both are lexically scoped. When a function binding is needed, i.e. the first element in a call, then the bindings are searched until the first occurrence of a binding which can be used as a function, either a function binding with right name and arity, or a variable binding, which may be a lambda. For a variable binding only the variable bindings are searched. While this is not consistent with Scheme (or CL) it is simple and does not need extra syntax. An alternative would be to split the two into separate variable and function bindings and have extra syntax to use a variable binding. For example (funcall func arg ... ). CL with the function quotes. Core solves this by having separate bindings and special to have only one apply: apply _F (...) and apply _F/3 ( a1, a2, a3 ). Function shadowing ------------------ Unqualified functions shadow as stated above in the following order within a module: Predefined BIFs (same as in vanilla Erlang) Imports Top-level defines Flet/fletrec This means that it is perfectly legal to shadow BIFs by imports, BIFs/imports by top-level functions and BIFs/imports/top-level by letrecs. In this respect there is nothing special about BIfs, they just behave as prefined imported functions, a whopping big (import (from erlang ...)). EXCEPT that we know about guard BIFs and expression BIFs. If you want a private version of spawn then define it, there will be no warnings. *CAVEAT* This does not hold for the supported Core forms. These can be shadowed by imports or redefined but the compiler will *always* use the Core meaning and never an alternative. Silently! Module definition ----------------- (define-module name (export (f 2) (g 1) ... ) (import (from mod (f1 2) (f2 1) ... ) (rename mod ((f1 2) sune) ((f2 1) kurt) ... )) (import (prefix mod mod-prefix))) - NYI Can have multiple export and import declarations within module declaration. Macros ------ Macro calls are expanded in both body and patterns. This can be very useful to have both make and match macros, but be careful with names. Can define simple expansions like in Scheme, but expansions are definitely NOT hygenic. Doubt if they ever really can be. Can define macros for all Core forms but they will never be used. (define-syntax name (syntax-rules (((pat ... ) expansion) ((pat ... ) expansion)))) The patterns are only the arguments not the macro name. E.g. (define-syntax andalso (syntax-rules (() 'true) ((e) e) ((e . es) (case e ('true (andalso . es)) ('false 'false))))) Also have macros where the bodies are evaluated as in "normal" macros. (define-syntax name (macro (pat [guard] ... ) (pat ... ))) The pattern is matched against the arguments of the macro call and a clause is chosen. Its body is evaluated and the expression returned is inserted into the code. Like a normal lisp macro except we use pattern macthing to select clause. E.g. (define-syntax andalso (macro ((e) e) ((e . es) `(case ,e ('true (andalso . ,es)) ('false 'false))) (() `'true))) Yes we have backquote. *CAVEAT* While it is perfectly legal to define a Core form as a macro these will silently be ignored by the compiler. Records (So Far) ---------------- Records are tuples with the record name as first element and the rest of the fields in order exactly like "normal" Erlang records. (define-record name field1 filed2 ... ) Will create macros for creation, matching and field access. (define-record person name age) => (make-person name age) (is-person rec) (match-person name age) - Used in patterns to test and extract (person-name rec) (set-person-name rec name) (person-age rec) (set-person-age rec age) This is basically what R6RS provides although they have an extra layer accessible. Not really that useable as it stands. Records (NYI) ------- (define-record name field (field default-val) ... ) Will create access functions/macros for creation and accessing fields. The make form takes optional argument pairs field-name value to get non-default values. E.g. for (define-record person (name "") age) => (make-person ... ) (match-person ... ) (person-name r) (set-person-name r name) (person-age r) (set-person-age r age) (make-person name "Robert" age 54) How to access them in patterns? Compiling a file ---------------- lfe_comp:file(file-name[, Options]) -> {ok,Mod,Warnings} | {error,Errors,Warnings} Compiles file file-name.lfe to BEAM file file-name.beam. Must be loaded explicitly in shell. Option 'binary' returns beam module as binary. Linenumbers in errors and warnings is the line of the beginning of the form in which the error occurs. lfe_comp:forms(Forms[, Options]) -> {ok,ModName,Binary} | {error,Errors,Warnings} Compiles list of forms into BEAM binary. Each "form" is {Form,LineNumber}. Linenumber only used for error messages. The LFE Shell ------------- There is now a simple LFE shell in which you can enter sexprs which are evaluated and the value printed. There are no user shell variables (scoping and lack of setq) Builtin shell functions: (c File [Options]) Compile and load an LFE file. Assumes default extension .lfe. (l Module ...) Load modules. (slurp File) Slurp in a source LFE file and makes all functions and macros defined in the file available in the shell. Only one file can be slurped at a time and slurping a new file removes all data about the previous one. (: c Command Arg ...) All the commands in the standard Erlang shell can be reached in this way. Builtin shell variables: +/++/+++ The three previous expressions input. */**/*** The values of the previous 3 expressions. - The current expression input. Starting the LFE shell: From a normal Erlang shell the best way to start the shell is by calling: 17> lfe_shell:server(). A modified user_drv.erl from the standard Erlang distribution has been provided which allows to start a specific local shell, not just the default Erlang shell. If this version of user_drv.beam is placed in the standard load path then giving the user switch commands: --> s lfe_shell --> c will create a job running the LFE shell and connect to it. This also works when starting a remote shell. Notes ----- NYI - Not Yet Implemented