Lecprog2, a new dialect of Lisp Author: Cristian Chidesa Email: [email protected] , [email protected] In my opinion, a disadvantage in Common Lisp programming is its evaluation mechanism which uses quote, backquote and comma operators. The quote is a way of protecting Lisp expressions from evaluation but, at each evaluation step of a multi-quoted expression, a quote is removed and the expression globally changes. Sometimes, we need to preserve intact some expressions through subsequent evaluations and quote and backquote operators don’t offer a good solution in this case. The expressions in Lecprog2 are built as in Common Lisp, every expression is either an atom (string, number, constant symbol) or a list. But unlike Lisp, Lecprog2 doesn't use quote and backquote operators. In Lecprog2 there exist two types of lists: lists delimited by (round) parenthesis (i.e. "(" and ")" ) and lists delimited by square brackets (i.e. "[" and "]" ). Both types of lists can contain atoms and other lists. Lists delimited by parenthesis are used for function calls while lists delimited by square brackets are used to represent data, function parameters, (local) variables within an iterative structure "iter_sequence" and clauses of instruction "filter" (instruction "filter" is similar to instruction "cond" in Common Lisp). Therefore a list can represent a generic tree structure containing function calls mixed with data. The axioms for evaluating expressions in Lecprog2 are given below (text in bold + italic + green color) but, firstly, let's see a few examples in Lecprog2: >>> ( ) --> error >>> [ ] --> [ ] //i.e. empty data list Lecprog2 doesn't have a special atom "nil". In Lecprog2, instead of atom "t" there exists the atom "true". Lecprog2 has also the atom "false" which is different from "[ ]" . >>> (is_atomic a) --> true >>> (is_atomic [a b c]) --> false >>> (is_atomic [ ]) --> false >>> (is_atomic false) --> true >>> (is_atomic true) --> true >>> (is_atomic error) --> true >>> (is_list a) --> false >>> (is_list [a b c]) --> true >>> (is_list [ ]) --> true >>> (cons a [b [c d] f]) --> [a b [c d] f] >>> (car [a b c d]) --> a >>> (cdr [a b c d]) --> [b c d] e. We can attach a value to an atom by using the function "set" but we need to use the function "get_value" to retrieve the value attached to that atom. "(" and ")") must be a standard function. Lists delimited by square brackets and containing only atoms excepting the special atom "nothing". an atom is evaluated to itself. no return value for function "set" >>> a --> a //no direct evaluation for an atom (!). "error" is used as result of an evaluation that gives error. Ex: >>> [a b c] --> [a b c] //no changes >>> [cons 2 [3 4]] --> [cons 2 [3 4]] //no changes The first item of a list delimited by (round) parenthesis (i. for simplicity. From now on. Evaluating a list delimited by square brackets (i. 2) Any call of "set" or "defun" functions always returns "nothing" or "error".e. "get_value" applied to a constant //returns that constant >>> (get_value "a string") --> "a string" //the same convention >>> (get_value [2 3 4]) --> error >>> (get_value car) --> error //by convention. . need to use function "get_value" >>> (get_value a) --> w >>> (get_value 7) --> 7 //by convention. "get_value" applied to // a reserved word returns error. In Lecprog2. The evaluation of such list (delimited by "(" and ")") means to evaluate the parameters of that function (which may entail recursive evaluation for parameters that are lists) and then apply that function to those evaluated parameters. >>> (set b c) --> nothing >>> ^b --> c >>> (set a ^b) --> nothing >>> ^a --> c >>> (set "a string" 7) --> error //by convention. we cannot attach //a constant value to a constant (number. string.e. are pure data and do not change through evaluation in Lecprog2. "a" being an atom or a function parameter or a (local) variable within an iterative structure "iter_sequence". or an user-defined function or an anonymous function (lambda expressions). Ex: >>> (set a w) --> nothing //i. "[" and "]") means to evaluate sequentially each item of that list (which implies recursive evaluation if such item is also a list) and the result will be the list containing these evaluated items. etc) >>> (set 10 7) --> error Note: 1) "nothing" and "error" are special atoms in Lecprog2. we'll write "^a" instead of "(get_value a)". are evaluated (if they exist). the parameters of that function call. Ex: >>> [ car [cdr [a b c d]] ] --> [car [cdr [a b c d]]] //no changes >>> ( car [cdr [a b c d]] ) --> cdr >>> [ car (cdr [a b c d]) ] --> [car [b c d]] >>> ( car (cdr [a b c d]) ) --> b >>> ( car [(cdr [x y z]) u v] ) --> (car [[y z] u v]) --> [y z] >>> (is_empty_list [ ]) --> true >>> (is_empty_list [a b c]) --> false >>> (is_empty_list an_atom) --> false //insert at index: >>> ( insert 0 y [b c d] ) --> [y b c d] >>> ( insert 1 y [b c d] ) --> [b y c d] >>> ( insert 1 nothing [b c d] ) --> [b c d] >>> ( insert 3 y [b c d] ) --> [b c d y] >>> ( insert 5 y [b c d] ) --> [b c d y] >>> ( insert 5 nothing [b c d] ) --> [b c d] >>> ( insert 1 (cdr [a b c]) [(cons x [u v]) (car [w u])] ) --> --> ( insert 1 [b c] [[x u v] w] ) --> --> [[x u v] [b c] w] //modify item at index: >>> ( set_item 0 y [b c d] ) --> [y c d] >>> ( set_item 2 y [b c d] ) --> [b c y] >>> ( set_item 2 nothing [b c d] ) --> [b c] >>> ( set_item 3 y [b c d] ) --> [b c d] //unchanged >>> ( set_item 3 nothing [b c d] ) --> [b c d] //unchanged //get item at index: >>> ( get_item 0 [[x u v] [b c] v] ) --> [x u v] >>> ( get_item 1 [[x u v] [b c] v] ) --> [b c] >>> ( get_item 3 [[x u v] [b c] v] ) --> nothing //--------------------------------------------------------- >>> ( apply + [3 4 5] ) --> 12 //the same as: // (+ 3 4 5) --> 12 >>> ( apply cdr [[3 4 5]] ) --> [4 5] //the same as: // (cdr [3 4 5]) --> [4 5] >>> ( apply cons [a [b c]] ) --> [a b c] //the same as: // (cons a [b c]) --> [a b c] A few more examples with atom "error": >>> (+ 5 [a b]) --> error //i. i.e.Note: The first item of a list delimited by (round) parenthesis is not evaluated.e. the result is the atom "error" >>> ( get_error_msg ) --> --> "all the arguments of function '+' must be numbers" >>> ( 3 2 ) --> error . only subsequent items from the list. (catch_error <some_expression>) or (catch_error <some_expression> <expression_if_error>) ). Examples: >>> [ 4 error x y ] --> [ 4 error x y ] //no changes >>> [ 4 (3 2) x y ] --> [ 4 error x y ] >>> ( + 5 (3 2) ) --> error >>> (catch_error error) --> nothing //by convention >>> [ 4 (catch_error error) x y ] --> [4 nothing x y] --> --> [4 x y] . etc). By convention.e. 3) that list represents a lambda expression. catch . >>> (catch_error error) --> nothing >>> (catch_error error <expression_if_error>) --> <expression_if_error> 2) that list represents a call of function "if" or a call of function "filter" ...e.>>> ( get_error_msg ) --> --> "the first item of a list enclosed by round parenthesis must be a standard function or a user named function or a lambda expression" >>> (set cdr "some_string") --> error >>> ( get_error_msg ) --> --> "an atom representing a reserved word cannot be the first parameter of function 'set'" The evaluation rule in Lecprog2 should be supplemented with statements about atoms "error" and "nothing": If an item of a list enclosed by square brackets is atom "nothing" then it will be removed from that list during evaluation. a list enclosed by "(" and ")") is the atom "error" then that list will be evaluated to "error" excepting the following cases: 1) that list represents a call of function "catch_error" ( i. and can be used to prevent the propagation of atom "error" to the top level of the tree structure expression during evaluation.. By convention: >>> (car [ ]) --> nothing >>> (cdr [ ]) --> [ ] Ex: >>> >>> >>> >>> >>> >>> >>> [ + 5 nothing 2 ] --> [ + 5 2 ] [ nothing x nothing u w ] --> [x u w] [ nothing nothing nothing ] --> [ ] [ (set x 11) (set y 21) z ] --> [z] [ (set x 11) (set y 21) ] --> [ ] (car [nothing (+ 3 4)]) --> (car [7]) --> 7 (car [(set a 4) (* ^a 2)]) --> (car [8]) --> 8 If an item of a list representing a function call (i." in Java (C#.. Note: 1) The special function "catch_error" will catch the error. C++. similar to "try . no return value for function "defun" Ex: >>> (sum [1 2 7]) --> 10 . >>> >>> >>> >>> >>> --> (catch_error (3 2)) --> nothing [ 4 (catch_error (3 2)) x y ] --> [4 x y] (catch_error (+ 2 2)) --> 4 ( catch_error error (car [w z]) ) --> w ( catch_error (3 2) ( get_error_msg ) ) --> "the first item of a list enclosed by round parenthesis must be a standard function or a user named function or a lambda expression" >>> ( catch_error (+ 2 2) ( get_error_msg ) ) --> 4 //no error >>> ( catch_error (+ 2 2) (3 2) ) --> 4 //no error. in this case. "(30 40 50)".e. // "(3 2)" is not evaluated >>> ( catch_error (3 2) (car [w z]) ) --> w >>> [ 4 (catch_error (3 2) (car [w z])) x y ] --> [4 w x y] //--------------------------------------------------------- 2) Functions "if" and "filter" are exceptions to the evaluation rule: >>> ( if (< 4 5) (car [x1 x2]) (cons u [v]) ) --> x1 >>> [ if (< 4 5) (car [x1 x2]) (cons u [v]) ] --> [if true x1 [u v]] >>> ( if (< 2 3) "ok" error ) --> "ok" //clause "else" is not evaluated >>> ( if (< 4 5) error [30 40 50] ) --> error >>> ( if (< 4 5) [30 40 50] error ) --> [30 40 50] //no error because // the else clause. is not evaluated >>> ( filter [ (is_number 10) "10 is a number" ] [ default error ] ) --> "10 is a number" //no error because the clause "default" is not //evaluated //--------------------------------------------------------- User functions in Lecprog2 As in Common Lisp. is not evaluated >>> ( if (< 4 5) (30 40 50) [30 40 50] ) --> error //that's because // the first item in (30 40 50) is the constant 30 (not a function) >>> ( if (< 4 5) [30 40 50] (30 40 50) ) --> [30 40 50] //no error // because the else clause. Example (recursive function): >>> ( defun sum [ lst ] ( filter [ (is_empty_list lst) 0] [ default (+ (car lst) (sum (cdr lst))) ] ) ) --> nothing //i. the user can define its own functions in Lecprog2 by using primitive "defun". The parameters of a user-defined function are enclosed by square brackets. the atom "error". //--------------------------------------------------------- Suppose that Lecprog2 has a standard function "eq" which tests the equality of two atoms but doesn't work for lists: >>> (eq a a) --> true >>> (eq a b) --> false >>> (eq [a b] [a b]) --> false >>> (eq [ ] [ ]) --> true >>> (eq <any_atom> <any_list>) --> false >>> (eq <any_list> <any_atom>) --> false >>> ( defun equal [ itm1 itm2 ] ( filter [ (is_atomic itm1) (eq itm1 itm2) ] [ (is_atomic itm2) false ] [ (equal (car itm1) (car itm2)) (equal (cdr itm1) (cdr itm2)) ] [ default false ] ) ) --> nothing Ex: >>> (equal [a [[b] c]] [a [[b] c]]) --> true >>> (equal [a [[b] c]] [a [b c]]) --> false //--------------------------------------------------------- >>> (defun append [ l1 l2 ] (filter [ (is_empty_list l1) l2 ] [ default (cons (car l1) (append (cdr l1) l2)) ] ) ) --> nothing Ex: >>> (append [a b] [x y z]) --> [a b x y z] //--------------------------------------------------------- Parameters of functions can be in their turn functions: >>> ( defun f1 [ x ] (+ x 1) ) --> nothing >>> ( defun q2 [ h u v ] (* (apply h [u]) (apply h [v])) ) --> nothing >>> (q2 f1 4 7) --> 40 //--------------------------------------------------------- A few more examples with user-defined functions in Lecprog2: >>> ( defun is_member [ x lst ] ( filter [ (is_empty_list lst) false ] [ (equal x (car lst)) true ] [ default (is_member x (cdr lst)) ] ) ) --> nothing . then" ( filter [ (is_reserved_word x) error ] //like "car"..e. "equal".3]] ) --> --> (apply * [7 1]) --> 7 //--------------------------------------------------------- . [ (is_number ^x) ^x ] [ default error ] ) // clause "else" ( apply (get_item 1 x) //i. "cdr". etc. Ex: >>> ( is_member [a [b]] [x y [a [b]] c d] ) --> true >>> ( is_member [a [b]] [x y a [b] c d] ) --> false //--------------------------------------------------------- >>> ( defun rec_reverse [ lstSource lstRev ] ( filter [ (is_empty_list lstSource) lstRev ] [ default (rec_reverse (cdr lstSource) (cons (car lstSource) lstRev) ) ] ) ) --> nothing >>> ( defun reverse [ x ] ( if (is_atomic x) x (rec_reverse x [ ]) ) ) --> nothing Ex: >>> ( reverse [a b c d] ) -> [d c b a] Note: Functions "append". "is_member". "rec_reverse" can be defined in Lecprog2 in the same way as in Common Lisp. //--------------------------------------------------------- >>> ( defun eval_infix [ x ] ( if (is_atomic x) // clause "if . the arithmetic operator [ (eval_infix (get_item 0 x)) (eval_infix (get_item 2 x)) ] ) ) ) --> nothing Ex: >>> (set a 4) --> nothing >>> ( eval_infix [[a + 3] * [a .. (sqr ^x) (sqr ^y)) ] (< ^u 0) [u (sqr ^x)] [u (* 12 ^x)] ) ] ) ) --> nothing >>> >>> >>> >>> (set c 4) --> nothing (set a 2) --> nothing (set b 3) --> nothing (f2 c a b) --> 5 >>> (set b 2) --> nothing >>> (f2 c a b) --> [c 24] In order to simplify the body of this function.(sqr x) (sqr y)) ] [ default (if (< ^u 0) [u (sqr x)] [u (* 12 x)] ) ] ) ) ) --> nothing Note: 1) Instruction "auto_eval" is an exception to the evaluation rule.(sqr y) (sqr x)) ] [ (> x y) (.Let’s consider the function: >>> (defun f2 [ u x y ] ( filter [ (< ^x ^y) [ (> ^x ^y) [ default (if (. evaluating without using “get_value” function): >>> (defun f2 [ u x y ] (auto_eval [x y] (filter [ (< x y) (. we could use a special function.(sqr ^y) (sqr ^x)) ] (. “auto_eval” which would allow a direct evaluation of the function parameters (i.e. //--------------------------------------------------------- Two important functions which can evaluate expressions written as pure data (only "[" and "]"): >>> ( defun eval_in_depth [ fn_lst data ] ( filter [ (is_atomic data) data ] [ (is_member (car data) fn_lst) (apply (car data) (eval_data_list fn_lst (cdr data)) ) . ^y ^x) ) ( exit_sequence (. for example when processing big lists. >>> ( iter_sequence [ x y ] ( set x 4 ) ( set y 5 ) ) --> nothing >>> ( iter_sequence [ x y ] ( set x 4 ) ( set y ^x ) ( exit_sequence (* 3 ^y) ) ) --> 12 >>> ( iter_sequence [ x y ] ( set x 8 ) ( set y 3 ) ( if (< ^x ^y) ( exit_sequence (.^x ^y) ) ) ( exit_sequence 100 ) //note: this instruction is never evaluated ) --> 5 .] [ default (eval_data_list fn_lst data) ] ) ) --> nothing >>> ( defun eval_data_list [ fn_lst data_lst ] ( filter [ (is_empty_list data_lst) []] [ default (cons (eval_in_depth fn_lst (car data_lst)) (eval_data_list fn_lst (cdr data_lst)) ) ] ) ) --> nothing Ex: >>> ( eval_in_depth [+ * -] [* [+ 5 3] [.7 4]] ) --> [* 8 3] >>> ( eval_in_depth [car cdr] [car [cdr [u v w]]] ) --> v >>> ( eval_data_list [cons car] [[cons z [m]] [car [a b]] [cdr [u v w]]] ) --> --> [[z m] a [cdr [u v w]]] //---------------------------------------------------------- Lecprog2 needs also iterative structures because there are a lot of tasks that can be better handled iteratively than recursively.7 4]] ) --> 24 >>> ( eval_in_depth [+ -] [* [+ 5 3] [. //---------------------------------------------------------- Anonymous functions (lambda expressions) - exception to the evaluation rule. ) ) --> 25 //i. >>> ( iter_sequence [ x s ] ( set x 1 ) ( set s 0 ) ( do ( if (>= ^x 10) (exit_sequence ^s) ) ( set s (+ ^s ^x) ) //i.: 1 + 3 + 5 + 7 + 9 --> 25 Note: Instructions "iter_sequence" and "do" are exceptions to the evaluation rule.e. we'll denote lambda by "@" ("@" is a special atom): >>> (@ [u] (+ u 1)) --> (@ [u] (+ u 1)) //no changes >>> ( (@ [u] (+ u 1)) 4 ) --> 5 >>> ( defun q2 [ h u v ] (* (apply h [u]) --> nothing ) >>> ( q2 --> ( * (@ [u] (+ u 1)) ( apply ( apply ) --> 10 >>> >>> >>> >>> >>> ( ( ( ( is_list (@ is_atomic is_lambda is_lambda 1 (apply [v])) 4 ) --> (@ [u] (+ u 1)) (@ [u] (+ u 1)) [x] (* (@ [x] (@ [x] [@ [x] h [1] ) [4] ) 2 x)) ) --> false (* 2 x)) ) --> false (* 2 x)) ) --> true [* 2 x]] ) --> false ( cons (@ [x] (* 2 x)) ["a string" some_atom 21] ) --> [ (@ [x] (* 2 x)) >>> ( get_item 2 ["a string" "a string" some_atom (@ [x] (* 4 x)) 21 ] (@ [x] (+ x 1)) ) --> (@ [x] (+ x 1)) >>> ( car (@ [x] (* 2 x)) ) --> error >>> ( get_error_msg ) --> --> "the parameter of function 'car' must be a list enclosed by square brackets" >>> ( cdr (@ [x] (* 2 x)) ) --> error >>> ( get_error_msg ) --> --> "the parameter of function 'cdr' must be a list enclosed by square brackets" 32] . x = x + 2.e. s = s + x. ( set x (+ ^x 2) ) //i.e. . a "turn_lambda_into_data" call acts only on the enclosing lambda expression and on all internal function calls excepting internal lambda expressions (if there exist).] . "eval_in_depth".y 1)) ) --> nothing 3) Primitive "turn_lambda_into_data" turns a lambda expression into data.1 y) (. >>> ( eq_lambda (@ [x] (* 2 x)) (@ [y] (* 2 y)) ) --> true Note: The definitions of functions "equal". In order to endow Lecprog2 with an efficient mechanism of reflection.1 y) (. All "(" will be replaced with "[# " and the corresponding ")" will be replaced with "]".e.)").>>> ( get_item 2 (@ [x] (* 2 x)) ) --> error >>> ( get_error_msg ) --> --> "the second parameter of function 'get_item' must be a list enclosed by square brackets" Function "eq_lambda" tests the equality of two lambda expressions after renaming bound variables.e "(@ [.y 1)) ) ) --> nothing //i. Ex: >>> ( function_from_lambda g4 ( @ [y] (if (< y 1) (. "(" and ")" of internal lambda expressions. unlike the older versions. the definition of function "g4" was updated Note: This is equivalent with: >>> ( defun g4 [y] (if (< y 1) (. .. which gives the definition of a user named function as a lambda expression. which defines a function whose name is the first parameter and whose expression is given by a lambda expression as second parameter. So. Ex: >>> ( defun q2 [ h u v ] (* (apply h [u]) (apply h [v])) ) --> nothing >>> ( get_lambda q2 ) --> --> ( @ [ h u v ] (* (apply h [u]) (apply h [v])) ) 2) "function_from_lambda" . "eval_data_list" need to be modified to take account of lambda expressions. I used other primitives for reflection in Lecprog2 which focus especially on lambda expressions (anonymous functions). It takes one parameter which must be a lambda expression (i.. it would be useful to have the following primitives: 1) "get_lambda" . //--------------------------------------------------------- Reflection in Lecprog2 In this version of pdf doc about Lecprog2.. but with one exception. But the expression resulting from a first "turn_lambda_into_data" call can be parsed (as being data) and primitive "turn_lambda_into_data" can be used again. let's call them auxiliary symbols (or unregistered symbols. Ex: >>> ( turn_lambda_into_data (@ [x y] (+ (sqr x) (sqr y))) ) --> --> [ # @ [_x21 _y22] [# + [# sqr _x21] [# sqr _y22]] ] Note: "#" is a special atom. unlike atoms which are registered symbols within Lecprog2 environment). internal lambda expressions "(@ [y] (* 2 y))" and "(@ [y] (* 5 y))" are not affected by this "turn_lambda_into_data" call. if necessary. >>> ( cons # [car [b c]] ) --> [# car [b c]] >>> ( car [# + 3 4] ) --> # >>> ( set # <some_expression> ) --> error Another example: >>> ( turn_lambda_into_data ( @ [x] ( filter [ (< x 1) (@ [y] (* 2 y)) ] [ (< x 3) (iter_sequence [w] (set w 7) (exit_sequence ^w) ) ] [ default (@ [y] (* 5 y)) ] ) ) ) --> --> [# @ [_x23] [# filter [ [# < _x23 1] (@ [y] (* 2 y)) ] [ [# < _x23 3] [# iter_sequence [_w24] [# set _w24 7] [# exit_sequence [# get_value _w24]] ] ] [ default (@ [y] (* 5 y)) ] ] ] Note: 1) In the example above. then we need to use a special type of symbols in Lecprog2. in order to turn any possible internal lambda expression into data. 2) If the parameter in a "turn_lambda_into_data" call is a lambda expression having parameters like in the examples above. the name of an auxiliary symbol begins with "_". We'll suppose also that Lecprog2 environment manages a global counter which . By convention. parameter "x" was replaced with the auxiliary symbol "_x21" and parameter "y" was replaced with the auxiliary symbol "_y22" . - an expression representing the future lambda expression's body.. The lambda expression's parameters and local variables of the enclosing lambda expression will be renamed to auxiliary symbols by using new values of this counter. Ex: >>> ( set atom1 (new_aux_symb "aux") ) --> nothing >>> ^atom1 --> _aux25 //the global counter value is 25 now By convention. All these replacements (i. excepting inside any existing internal lambda expressions (i. existing internal lambda expressions are ignored).e "[# . If "_x" is an auxiliary symbol occurring in both (future) lambda expression's parameter list and (future) lambda expression's body then "_x" will be replaced with "x" inside both of them. afterwards. an auxiliary symbol will behave like an atom excepting when it is used with functions "set" and "get_value": >>> ( cons _aux31 [a b c] ) --> [_aux31 a b c] >>> ( get_item 1 [b _aux17 "some string" _aux5] ) --> _aux17 >>> ( is_atomic _aux31 ) --> true >>> ( is_aux_symb _aux31 ) --> true >>> ( is_aux_symb [a b c] ) --> false >>> ( is_aux_symb "a string" ) --> false >>> ( set _aux31 "some string as value" ) --> error >>> ( get_error_msg ) --> --> "the first parameter of function 'set' cannot be an auxiliary symbol" >>> ( get_value _aux31 ) --> error >>> ( get_error_msg ) --> --> "the parameter of function 'get_value' cannot be an auxiliary symbol" //--------------------------------- 4) Primitive "get_lambda_from_data" constructs a lambda expression from data. It takes two parameters: - a list of auxiliary symbols (which can be an empty data list) representing the future lambda expression's parameter list. future lambda expression's body) all the "[# " with "(" and the corresponding "]" with ")".] " <-- "( .is automatically incremented at each call of function "turn_lambda_into_data" for a lambda expression or at each call of a primitive "new_aux_symb".. the global counter value for auxiliary symbols was 20 before the first "turn_lambda_into_data" call and became 24 after the second call.e. In the second example. Thus.e. )" ) are made without evaluation. In the examples above.. New auxiliary symbols can also be generated by using primitive "new_aux_symb". the result of a "get_lambda_from_data" call will be a lambda expression . "get_lambda_from_data" replaces within its second parameter (i.. parameter "x" was replaced with the auxiliary symbol "_x23" and local variable "w" of instruction "iter_sequence" was replaced with the auxiliary symbol "_w24" . all the occurrences of "x" from inside the lambda expression's body will be bound to parameter "x" (from lambda expression's parameter list). In the first example. Ex: >>> ( get_lambda_from_data [] [atom1 "a string" --> ( @ [] [atom1 "a string" 12] ) >>> ( get_lambda_from_data [_y _z] [# + [# sqr _y] [# sqr _z]] ) --> ( @ [y z] (+ (sqr y) (sqr z)) ) Another example: >>> ( get_lambda_from_data 12] ) --> [_x] [# filter [ [# < _x 1] (@ [y] (* 2 y)) ] [ [# < _x 3] [# iter_sequence [_w] [# set _w 7] [# exit_sequence [# ] ] [ default (@ [y] (* 5 y)) ] ] get_value _w]] ) --> (@ [x] ( filter [ (< x 1) (@ [y] (* 2 y)) ] [ (< x 3) (iter_sequence [w] (set w 7) (exit_sequence ^w) ) ] [ default (@ [y] (* 5 y)) ] ) ) //--------------------------------- Let's suppose that we obtained programmatically in Lecprog2 an "iter_sequence" expression represented as pure data and we want to run this source code. we'll use primitive "get_lambda_from_data" in order to create a lambda expression whose body is exactly that "iter_sequence" expression. we can store that lambda expression as value of an atom "atom_for_lambda" and then. The lambda expression's parameter list will be empty. For convenience. we'll make a call of that lambda expression by using function "apply": >>> ( set atom_for_lambda . Firstly.constructed in this way. _x] ] . the use of primitives "get_lambda". "new_aux_symb". a new programming language would be really useful if that language could offer a maximum of flexibility and power for programming by using a minimal set of conventions and programming rules. no parameters for the future lambda expression iter_sequence [_x] [# set _x 25] [# exit_sequence [# (@ [y] (* 2 ^y)) //end of "[# iter_sequence . I intend to define the axioms of Lecprog2 following this principle." ) --> nothing >>> ( apply --> (apply (@ ^atom_for_lambda [] ) --> [] ( iter_sequence [ x ] (set x 25) ( exit_sequence ((@ [y] (* 2 ^y)) x) ) ) ) [] ) --> (* 2 25) --> 50 //--------------------------------------------------------- Remark: Obviously. Conclusion: In my opinion.( get_lambda_from_data [] [# ] ) //i. "get_lambda_from_data". to write functions which modify other functions or even to write functions which modify themselves at runtime.e "[" and "]" ) which may possibly start with "#" (i. "function_from_lambda" could allow us to programmatically define new functions at runtime. So.e. pure data).e. "turn_lambda_into_data".. we can build programmatically at runtime expressions containing only atoms and lists enclosed by square brackets (i. Constraint Programming and other branches of Artificial Intelligence. This feature could make Lecprog2 really suitable for Machine Learning..