SoFunction
Updated on 2025-04-10

The ultimate beauty - A hundred lines of code to realize a new intelligent language page 2/6


We can represent this function in this way
[label,subst,[lambda,[x,y,z],
               [cond,[[atom,z],
                      [cond,[[eq,z,y],x],
                            true,z]]],
                     [true,[cons,[subst,x,y,[car,z]],
                               [subst,x,y,[cdr,z]]]]]]]
label = function(funName, funDef)
{
 __funList.push(funName);
 return (funDef);
};

Let's briefly record f=[label,f,[lambda,[...],e]] as
[defun,f,[...],e]
defun = function(funName, args, code)
{
 __funList.push(funName);
 if(code instanceof Array)
 {
  var fun = new Function(args, 
   "for(var i = 0; i < ; i++) arguments[i] = (arguments[i]);return ("+()+");");
  var globalFuncName = __funList.pop();
  fun._funName = globalFuncName;
  if(globalFuncName != null)
   self[globalFuncName] = fun;
  return fun;
 }
 return [];
};

So
[defun,subst,[x,y,z],
  [cond,[[atom,z],
         [cond,[[eq,z,y],x],
               [true,z]]],
     [true,[cons,[subst,x,y,[car,z]],
                  [subst,x,y,[cdr,z]]]]]]
By chance we see here how to write the default clause of a cond expression. The first element is 't clause will always succeed. So
[cond,[x,y],[[_,true],z]]
Equivalent to what we write in some languages
if x then y else z 
For function calls, it has the following structure: [FunName,[_,args]]
where FunName is the function name, [_,args] is the specified parameter reference list args
Note that [FunName,args] is also legal, but it is different from [FunName,[_,args]]. For the former, the instruction calculates the value of args before being called, and substitutes the calculated value as a parameter list into the function calculation (the expected args calculation result is List), while the args parameter list of the latter is calculated only when the function instruction is called.
So far we are happy to see that LispScript can be extended without relying on javascript
Now we can directly define some new functions with LispScript:
Function: [isNull,x] tests whether its argument is an empty table.
(
 [defun,'isNull',['x'],
  [eq,'x',[_,NIL]]]
);

> [isNull,[_,a]]
[]
> [isNull. [_,[]]]
t
Function: [and,x,y] returns t If both its independent variables are t, otherwise return [].
(
 [defun,'and',['x','y'],
  [cond,['x',[cond,['y',true],[true,NIL]],
   [true,NIL]]]]
);

> [and,[atom,[_,a]],[eq,[_,a],[_,a]]]
t
> [and,[atom,[_,a]],[eq,[_,a],[_,b]]]
[]
Function: [not,x] returns t if its argument returns [], return [] if its argument returns t.
(
 [defun,'not',['x'],
  [cond,['x',NIL],
        [true,true]]]
);

> [not,[eq,[_,a],[_,a]]]
[]
> [not,[eq,[_,a],[_,b]]]
t
Function: [append,x,y] takes two tables and returns their connection.
(
 [defun,'append',['x','y'],
  [cond,[[isNull,'x'],'y'],
    [true,[cons,[car,'x'],['append',[cdr,'x'],'y']]]]]
);

> [append,[_,[a,b]],[_,[c,d]]]
[a,b,c,d]
> [append,[], [_,[c,d]]]
[c,d]
Function: [pair,x,y] takes two tables of the same length and returns a table composed of a two-element table. The two-element table is an element pair of x and y at the corresponding position.
(
 [defun,'pair',['x','y'],
  [cond,
   [[and,[isNull,'x'],[isNull,'y']],NIL], 
   [[and,[not,[atom,'x']],[not,[atom,'y']]],
    [append,[[[car,'x'],[car,'y']]],['pair',[cdr,'x'],[cdr,'y']]]
   ]]]
);

> [pair,[_,[x,y,z]],[_,[a,b,c]]]
[[x,a],[y,b],[z,c]]
[assoc,x,y] takes the atom x and the table y returned by the pair function, and returns the second element of the first table that meets the following conditions: its first element is x.
(
 [defun,'assoc',['x','y'],
  [cond,[[eq,[car,[car,'y']],'x'],[car,[cdr,[car,'y']]]],
   [[isNull,'y'],NIL],[true,['assoc','x',[cdr,'y']]]]]
);

> [assoc,[_,x],[_,[[x,a],[y,b]]]]
a
> [assoc,[_,x],[_,[[x,new],[x,a],[y,b]]]]
new
[ret,e]Returns the expression calculation result
(
 [defun,'ret',['e'],[car,['e']]]
);

[str,e]Returns a reference to the expression's calculation result
(
 [defun,'str',['e'],[_,[_,'e']]]
);

Let's take a look at why we need to define the ret function:
I think through the previous explanation and practical application, everyone has understood the importance of quotes, and it is easy to prove: [[_,e]] = [e]
The problem now is that we must define a referenced inverse function f, so that [f,[_,e]] = e
Obviously ret is such a function
[map,x,y] expects x to be an atom and y is a table. If [assoc,x,y] is not empty, return the value of [assoc,x,y], otherwise return x
(
 [defun,'map',['x','y'],
  [cond,[[isNull,[assoc,'x','y']],'x'],[true,[assoc,'x','y']]]]
);

[maplist,x,y] expects x and y to be both tables, and return a table composed of the result of finding [map,t,y] for each element in x t, which finds [map,t,y].
(
 [defun,'maplist',['x','y'],
  [cond,
   [[atom,[_,'x']],[map,'x','y']],
   [true,[cons,['maplist',[car,[_,'x']],'y'],['maplist',[cdr,[_,'x']],'y']]]
  ]
 ]
);

Therefore, we can define functions to join tables, replace expressions, etc. Maybe it is a beautiful representation, what about the next step? Now the surprise is here. We can write a function as an interpreter for our language: This function takes any Lisp expression as an independent variable and returns its value. As shown below:
(
 [defun,'_eval',['e','a'],
  [ret,[maplist,[_,'e'],'a']]
 ]
);

_eval.'s simplicity may exceed our original expectations, so we obtained a complete self-parser implemented by LispScript!
Let's go back and think about what this means. We get a very beautiful computational model here. With just quote, atom, eq, car, cdr, cons, and cond, we define the function_eval., which actually implements our language, using it to define and (or) dynamically generate any additional functions and various grammars we want (this is more important)
Other (slightly complex) extensions:
Below we define the assignment operation of variables [setq,paraName,paraValue]
(
 [defun,'setq',['para','val'],
  [ret,[defun,'para',[],[_eval,'val']]]]
);

Add logical operator or, [or,x,y] returns t if its argument has one of t, otherwise return []
(
 [defun,'or',['x','y'],
  [not,[and,[not,'x'],[not,'y']]]]
);

Add loop control foreach, [foreach,v,[paralist],[expr]]
Foreach expects list to be a table, and each atom in the table is taken as the parameter of expr for calculation, and returns the table of calculation results.
(
 [defun,'foreach',['v','list','expr'],
  [cond,
   [[isNull,'list'],[]],
   [true,[cons,[_eval,[_,'expr'],[['v',[car,'list']]]],['foreach','v',[cdr,'list'],[_,'expr']]]]
  ]
 ]
);

Add batch assignment operation let, [let,[[a1,v1],[a2,v2]...]]
(
  [defun,'let',['paralist'],
   [foreach,"'v'",'paralist',[_,[setq,[car,"'v'"],[car,[cdr,"'v'"]]]]]
  ]
);

Summarize
Now it's time to look back and see what exactly we did and what the meaning of doing so.
First, we implemented a simple downward recursive lexer using javascript, which can simply process each atom of a nested array, plus several helper functions (toEvalString(), Assert(), Element() and a stack that stores function names... Simply put, we implemented a new "functional" language with only dozens of lines of code?? The complete kernel of LispScript.
Next we define 7 kinds of original operations, namely quote, atom, eq, car, cdr, cons and cond
Then (relatively complex), we defined three types of tags used to describe and call functions, namely lambda, label and defun, so we successfully implemented the core environment of the LispScript language with less than a hundred lines of code.
Next (the next part can be completely independent of JavaScript) We use 7 original operators and function definition tags to define some new functions, namely: isNull, and, not, append, pair, assoc, ret and str
Then we were surprised to find that we could define our own "parser" with just one line of LispScript directive??_eval function
Finally, we define some slightly complex functions on this basis, including: or, setq, foreach and let. Some of the new functions bring us the ability to define variables and process loops in the new language. Together with some of the functions implemented earlier, a relatively complete basic environment is built.
Written at the end: LispScript and Lisp
In fact, we implemented LispScript in javascript according to the wonderful description of [ref. Paul Graham.]. There is no doubt that it is a Lisp (or a functional language in the Lisp style). Although it is still very simple in function, it does conform to the basic ideas of Lisp and has the basic characteristics of Lisp. Due to the characteristics of javascript array grammar, I replaced() in [] with [] and replaced spaces with commas as separator. Unlike the articles of [ref. Paul Graham] and the Lisp of some current standards (or relatively standard) is that I deliberately weakened the syntax structure of LispScript based on the flexibility of JavaScript, which makes LispScript more flexible and more convenient to implement, but the cost is a small part of maintainability and security.
Finally, LispScript has a lot to be improved, for example, most obviously it does not have basic numerical computing capabilities (relatively speaking, symbolic operation capabilities are relatively complete), and the verification and side effects of the legality of atomic operating parameters, continuous execution (it must be useful only with side effects), dynamic visualization, complex data structure support, and annotation grammar (this is very important!) are all lacking, but these functions "can all be surprisingly remediated with very little extra code."
Thanks to John McCarthy, this genius showed us decades ago a "ultimate beauty" that no one can surpass in the field of programming so far. He published an extraordinary paper in 1960, in which his contribution to programming was like Euclidean's contribution to geometry. 1 He showed us how to construct a complete programming language based on only a few simple operators and a notation representing functions. McCarthy called this language Lisp, meaning List Processing, because one of his main ideas was to use a simple data structure table (list) to represent code and data.
Thanks to Paul Graham, who used easy-to-understand language to present the roots and essence of Lisp to us, allowing us to be lucky enough to experience Lisp's "extraordinary beauty" at close range.
If you understand John McCarthy's eval, then you are not just understanding a stage in the history of programming languages. These ideas are still the semantic core of Lisp. So in a sense, learning John McCarthy's original work shows us what Lisp is. It is not so much a design by McCarthy, but his discovery. It is not born as a language for artificial intelligence, rapid prototyping or tasks of the same level. It is the result of your attempt to axiomatic calculations (one of).
Over time, intermediate languages, that is, languages ​​used by intermediate programmers, are consistently approaching Lisp. Therefore, by understanding eval, you are understanding what the mainstream computing model will look like in the future.
References
The Roots of Lisp Paul Graham. Draft, January 18, 2002.
LISt Primer Colin Allen & Maneesh  Feb 6, 2001.(
Previous page123456Next pageRead the full text