Lisp Flavoured Erlang ===================== Special syntactic rules ----------------------- #(e e ... ) - Constant tuples (expand into (tuple e e ...) ?) #b(e e ... ) - Binaries expands into (binary e e ... ) [ ... ] - 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) ...) ... ) (letrec ((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 ((#(x y z) [(when e)] ... ) ... ))] [(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 (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} -> #('ok x) error -> 'error {yes,[X|Xs]} -> #('yes (x . xs)) <<34,F/float>> -> (binary 34 (f float)) #b(34 (f float)) 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 and letrec. 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 Letrecs 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 ------ 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 cluase 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 and field access. (define-record person name age) => (make-person name age) (is-person rec) (person-name rec) (set-person-name rec name) (person-age rec) (set-person-age rec age) 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 ... ) (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. Notes ----- NYI - Not Yet Implemented