Note\{\{...\}\}isusetodenoteoptionalsyntax.Specialsyntacticrules-----------------------#b#o#d#x#23r-Basedintegers#(ee...)-Tupleconstants#b(ee...)-Binaryconstant,e...arevalidliteralssegments[...]-Allowedasalternativeto(...)SupportedCoreforms--------------------(quotee)(consheadtail)(care)(cdre)(liste...)(tuplee...)(binaryseg...)(lambda(arg...)...)(match-lambda((arg...)\{\{(whene...)\}\}...)-Matchesclauses...)(let((pat\{\{(whene...)\}\}e)...)...)(let-function((namelambda|match-lambda) - Only define local functions ... ) ... ) (letrec-function ((name lambda|match-lambda)-Onlydefinelocalfunctions...)...)(let-macro((namelambda-match-lambda)-Onlydefinelocalmacros...)...)(progn...)(iftesttrue-expr\{\{false-expr\}\})(casee(pat\{\{(whene...)\}\}...)...))(receive(pat\{\{(whene...)\}\}...)...(aftertimeout...))(catch...)(trye\{\{(case((pat\{\{(whene...)\}\}...)...))\}\}\{\{(catch(((tupletypevalueignore)\{\{(whene...)\}\}-Mustbetupleoflength3here!...)...)\}\}\{\{(after...)\}\})(funcallfuncarg...)(callmodfuncarg...)-CalltoMod:Func(Arg,...)(define-functionnamelambda|match-lambda) (define-macro name lambda|match-lambda)Definefunctions/macrosattop-level.Supportedmacroforms---------------------(:modfuncarg...)=>(call'mod'funcarg...)(?\{\{timeout\{\{default\}\}\}\})-Receivenextmessage,optionaltimeoutanddefaultvalue(++...)(list*...)(let*(...)...)-Sequentiallet's(flet((name(arg...)...)...)...)(flet*(...)...)-Sequentialflet's(fletrec((name(arg...)...)...)...)Definelocalfunctions,thiswillexpandtolambdaormatch-lambdadependingonstructureaswithdefun.(cond...)-Thenormalcond,with(?=patexpr)(andalso...)(orelse...)(funfuncarity)-funfunc/arity(funmodfuncarity)-funmod:func/arity(lc(qual...)...)-[expr|| qual ... ] (bc (qual ...) ...) - << expr || qual ... >> (match-spec ...) - ets:fun2ms(fun ( ) -> end) Common Lisp inspired macros --------------------------- (defun name (arg ...) ...) (defun name ((argpat ...) ...) ...) Define a toplevel function, this will expand to lambda or match-lambda depending on structure. (defmacro name (arg ...) ...) (defmacro name arg ...) (defmacro name ((argpat ...) ...) ...) Define a top-level macro, this will expand to lambda or match-lambda depending on structure. (defsyntax name (pat exp) ...) Define a top-level macro using Scheme inspired syntax-rules format. (macrolet ((name (arg ...) ...) ...) ...) (syntaxlet ((name (pat exp) ...) ...) ...) Define local macros in macro or syntax-rule format. (defmodule name ...) (defrecord name ...) Older Scheme inspired macros ---------------------------- (define (name arg ...) ...) (define name lambda|match-lambda)(define-syntaxname(syntax-rules(patexp)...)|(macro (pat body) ...)) (let-syntax ((name ...) ...) ...) (begin ...) (define-module name ...) (define-record name ...) 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(consxxs))<<34,F/float>>->(binary34(ffloat))[P|Ps]=All -> (= (cons 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. _ 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. Guards ------ Wherever a pattern occurs (let, case, receive, lc, etc.) it can be followed by an optional guard which has the form (when test ...). Guard tests are the same as in vanilla Erlang and can contain the following guard expressions: (quote e) (cons gexpr gexpr) (car gexpr) (cdr gexpr) (list gexpr ...) (tuple gexpr ...) (binary ...) (progn gtest ...) - Sequence of guard tests (if gexpr gexpr gexpr) (type-test e) (guard-bif ...) - Guard BIFs, arithmetic, boolean and comparison operators An empty guard, (when), always succeeds as there is no test which fails. This simplifies writing macros which handle guards. Bindings and Scoping -------------------- LFE is a Lisp-2 and has separate namespaces for variables and functions/macros. Both variables and functions/macros are lexically scoped. Variables are bound by lambda, match-lambda and let, functions are bound by top-level defun, flet and fletrec, macros are bound by top-level defmacro/defsyntax and by macrolet/syntaxlet. When searching for function both name and arity are used, a macro is considered to have any arity and will match all functions with that name. While this is not consistent with either Scheme (or CL) it is simple, usually easy to understand and fits Erlang quite well. It does, however, require using (funcall func arg ... ) like CL to call lambdas/match-lambdas (funs) bound to variables. 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 which results in the following order within a module, outermost to innermost: Predefined BIFs (same as in vanilla Erlang) Predefined LFE BIFs 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 fletrecs. 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 ----------------- (defmodule name (export (f 2) (g 1) ... ) (export all) ;Export all functions (import (from mod (f1 2) (f2 1) ... ) (rename mod ((f1 2) sune) ((f2 1) kurt) ... )) (import (prefix mod mod-prefix)) - NYI (attr-1 value-1 value-2) ... ) Can have multiple export and import declarations within module declaration. The (export all) declaration is allowed together with other export declarations and overrides them. Other attributes which are not recognised by the compiler are allowed and are simply passed on to the module and can be accessed through module_info/0-1. Parameterized modules --------------------- (defmodule (name par1 par2 ... ) ... ) Define a parameterized module which behaves the same way as in vanilla Erlang. For now avoid defining functions 'new' and 'instance'. 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. A macro is function of two argument which is a called with a list of the arguments to the macro call and the current macro environment. It can be either a lambda or a match-lambda. The basic forms for defining macros are: (define-macro name lambda|match-lambda)(let-macro((namelambda|match-lambda) ...) Macros are definitely NOT hygienic in any form. To simplify writing macros there are a number of predefined macros: (defmacro name (arg ...) ...) (defmacro name arg ...) (defmacro name ((argpat ...) body) ...) Defmacro can be used for defining simple macros or sequences of matches depending on whether the arguments are a simple list of symbols or can be interpreted as a list of pattern/body pairs. In the second case when the argument is just a symbol it will be bound to the whole argument list. For example: (defmacro double (a) `(+ ,a ,a)) (defmacro my-list args `(list ,@args)) (defmacro andalso ((list e) `,e) ((cons e es) `(if ,e (andalso ,@es) 'false)) (() `'true)) The macro definitions in a macrolet obey the same rules as defmacro. The macro functions created by defmacro and macrolet automatically add the second argument with the current macro environment with the name $ENV. This allows explicit expansion of macros inside the macro and also manipulation of the macro environment. No changes to the environment are exported outside the macro. User defined macros shadow the predefined macros so it is possible to redefine the built-in macro definitions. However, see the caveat below! Yes, we have the backquote. It is implemented as a macro so it is expanded at macro expansion time. Local functions that are only available at compile time and can be called by macros are defined using eval-when-compile: (defmacro foo (x) ... (foo-helper m n) ...) (eval-when-compile (defun foo-helper (a b) ...) ) There can be many eval-when-compile forms. Functions defined within an eval-when-compile are mutually recursive but they can only call other local functions defined in an earlier eval-when-compile and macros defined earlier in the file. Functions defined in eval-when-compile which are called by macros can defined after the macro but must be defined before the macro is used. Scheme's syntax rules are an easy way to define macros where the body is just a simple expansion. These are supported with defsyntax and syntaxlet. Note that the patterns are only the arguments to the macro call and do not contain the macro name. So using them we would get: (defsyntax andalso (() 'true) ((e) e) ((e . es) (case e ('true (andalso . es)) ('false 'false)))) N.B. These are definitely NOT hygienic. *CAVEAT* While it is perfectly legal to define a Core form as a macro these will silently be ignored by the compiler. Extended cond ------------- Cond has been extended with the extra test (?= pat expr) which tests if the result of expr matches pat. If so it binds the variables in pat which can be used in the cond. A optional guard is allowed here. An example: (cond ((foo x) ...) ((?= (cons x xs) (when (is_atom x)) (bar y)) (fubar xs (baz x))) ((?= (tuple 'ok x) (baz y)) (zipit x)) ... ) Records ------- Records are tuples with the record name as first element and the rest of the fields in order exactly like "normal" Erlang records. As with Erlang records the default default value is 'undefined'. (defrecord name field (field default-value) ... ) Will create access functions/macros for creation and accessing fields. The make, match and set forms takes optional argument pairs field-name value to get non-default values. E.g. for (defrecord person (name '"") (address '"") age) => (make-person \{\{field value\}\} ... ) (match-person \{\{field value\}\} ... ) (is-person r) (emp-person \{\{field value\}\} ... ) (set-person r \{\{field value\}\} ... ) (person-name r) (set-person-name r name) (person-age r) (set-person-age r age) (person-address r) (set-person-address r address) (make-person name '"Robert" age 54) Will create a new person record with the name field set to "Robert", the age field set to 54 and the address field set to the default "". (match-person name name age 55) Will match a person with age 55 and bind the variable name to the name field of the record. Can use any variable name here. (is-person john) Test if john is a person record. (emp-person age '$1) Create an Ets Match Pattern for record person where the age field is set to $1 and all other fields are set to '_. (person-address john) Return the address field of the person record john. (set-person-address john '"back street") Sets the address field of the person record john to "back street". (set-person john age 35 address '"front street") In the person record john set the age field to 35 and the address field to "front street". Binaries/bitstrings ------------------- A binary is (binary seg ... ) where seg is byte string (val integer|float|binary|bitstring|bytes|bits(sizen)(unitn)big-endian|little-endian|native-endian|little|native|big signed|unsigned)Valcanalsobeastringinwhichcasethespecifierswillbeappliedtoeverycharacterinthestring.Asstringsarejustlistsofintegersthesearealsovalidhere.Inabinaryconstantallliteralformsareallowedoninputbuttheywillalwaysbewrittenasbytes.List/binarycomprehensions--------------------------List/binarycomprehensionsaresupportedasmacros.Thesyntaxforlistcomprehensionsis:(lc(qual...)expr...)wherethefinalexprisusedtogeneratetheelementsofthelist.Thesyntaxforbinarycomprehensionsis:(bc(qual...)expr...)wherethefinalexprisabitsegexprandisusedtogeneratetheelementsofthebinary.Thesupportedqualifiers,inbothlist/binarycomprehensionsare:(<-pat\{\{guard\}\}list-expr)-Extractelementsfromalistexpression(<=bin-pat\{\{guard\}\}binary-expr)-Extractelementsfromabinary/bitsexpression(?=pat\{\{guard\}\}expr)-Matchtestandbindvariablesinpatexpr-NormalbooleantestSomeexamples:(lc((<-vl1)(when(>v5))(==(remv2)0))v)returnsalistofalltheevenelementsofthelistl1whicharegreaterthan5.(bc((<=(ffloat(size32))b1);No wrapping, only bitseg needed(>f10.0))(:iofwrite'"~p\n"(listf))(ffloat(size64)));No wrapping, only bitseg neededreturnsabinaryoffloatsofsize64offloatswhicharelargerthan10.0fromthebinaryb1andofsize32.Thereturnednumbersarefirstprinted.N.B.Awordofwarningwhenusingguardswhenextractingelementsfromabinary.Whenamatch/guardfailsforabinarynomoreattemptswillbemadetoextractdatafromthebinary.Thismeansthatevenifavaluecouldbeextractedfromthebinaryiftheguardfailsthisvaluewillbelostandextractionwillcease.Thisis*NOT*thesameashavingfollowingbooleantestwhichmayremoveanelementbutwillnotstopextraction.Usingaguardisprobablynotwhatyouwant!NormalvanillaErlangdoesthesamethingbutdoesnotallowguards.ETSandMnesia--------------Apartfrom(emp-record...)macrosforETSMatchPatterns,whicharealsovalidinMnesia,LFEalsosupportsmatchspecificationsandQueryListComprehensions.Thesyntaxforamatchspecificationisthesameasformatch-lambdas:(match-spec((arg...)\{\{(whene...)\}\}...)-Matchesclauses...)Forexample:(:etsselectdb(match-spec([(tuple_ab)](when(>a3))(tuple'okb))))Itisamacrowhichcreatesthematchspecificationstructurewhichisusedinets:selectandmnesia:select.Thesamematch-specmacrocanalsobeusedwiththedbgmodule.Thesamerestrictionsastowhatcanbedoneapplyasforvanillamatchspecifications:-ThereisonlyalimitednumberofBIFswhichareallowed-Therearesomespecialfunctionsonlyforusewithdbg-Forets/mnesiaittakesasingleparameterwhichmustatupleoravariable-FordbgittakesasingleparameterwhichmustalistoravariableN.B.thecurrentmacroneitherknowsnorcareswhetheritisbeingusedinets/mnesiaorindbg.Itisuptotheusertogetthisright.Macros,especiallyrecordmacros,canfreelybeusedinsidematchspecs.*CAVEAT*Somethingswhichareknownnottoworkinthecurrentversionareandalso,orelseandrecordupdates.QueryListComprehensions-------------------------LFEsupportsQLCsformnesiathroughtheqlcmacro.IthasthesamestructureasalistcomprehensionandgeneratesaQueryHandleinthesamewayaswithqlc:q([...]).Thehandlecanbeusedtogetherwithallthecombinationfunctionsinthemoduleqlc.Forexample:(qlc(lc((<-(tuplekv)(:etstablee2))(==ki))v)\{\{Option\}\})Macros,especiallyrecordmacros,canfreelybeusedinsidequerylistcomprehensions.*CAVEAT*SomethingswhichareknownnottoworkinthecurrentversionarenestedQLCsandlet/case/recievewhichshadowvariables.PredefinedLFEfunctions------------------------Thefollowingmoreorlessstandardlispfunctionsarepre-defined:(<arith_op>expr...)(<comp_op>expr...)Thestandardarithmenticoperators,+-*/,andcomparisonoperators,>>=<=<==/==:==/=,cantakemultipleargumentsthesameastheirstandardlispcounterparts.Thisisstillexperimentalandimplementedusingmacros.Theydo,however,behavelikenormalfunctionsandevaluateALLtheirargumentsbeforedoingthearithmetic/comparisonsoperations.(aconskeyvaluelist)(assockeylist)(rassocvaluelist)Thestandardassociationlistfunctions.(substnewoldtree)(subst-ifnewtesttree)(subst-if-notnewtesttree)Thestandardsubstituitionfunctions.(macroexpand-1expr\{\{environment\}\})IfExprisamacrocall,doesoneroundofexpansion,otherwisereturnsExpr.(macroexpandexpr\{\{environment\}\})Returnstheexpansionreturnedbycallingmacroexpand-1repeatedly,startingwithExpr,untiltheresultisnolongeramacrocall.(macroexpand-allexpr\{\{environment\}\})Returnstheexpansionfromtheexpressionwhereallmacrocallshavebeenexpandedwithmacroexpand.NOTEthatwhennoexplicitenvironmentisgiventhemacroexpandfunctionsthenonlythedefaultbuilt-inmacroswillbeexpanded.Insidemacrosandintheshellthevariable$ENVisboundtothecurrentmacroenvironment.(evalexpr\{\{environment\}\})Evaluatetheexpressionexpr.Notethatonlythepre-definedlispfunctions,erlangBIFsandexportedfunctionscanbecalled.Alsonolocalvariablescanbeaccessed.Toaccesslocalvariablestheexprtobeevaluatedcanbewrappedinaletdefiningthese.Forexampleifthedatawewishtoevaluateisinthevariableexpranditassumesthereisalocalvariable"foo"whichitneedstoaccessthenwecouldevaluateitbycalling:(eval`(let((foo,foo)),expr))Notes-----NYI-NotYetImplementedN.B.-Notabene(notewell)