Commits

rxe committed f581b9b

how to lose friends and deter people

  • Participants
  • Parent commits 284334b

Comments (0)

Files changed (1)

File doc/idea.txt

+Evaluation rules and all that
+=============================
+
+some background
+---------------
+
+Basically, lists are evaluated in lisp, and the first argument of the list specifies some rule as to what the
+evaluation rule is.  Whether it is function, a special form or a macro.  One requires a great deal of context of what
+is what, when reading other peoples code.
+
+The major reason for this is that functions, or anything this 'appliable', make up the large majority of these first arguments.
+
+However, it is hard to second guess when that first argument is a macro.  And since a lot of builtin flow of control
+structures are written as macros, there is a huge amount of ambiguity.
+
+For instance in scheme one can do (racket code):
+
+"""
+#lang scheme/base
+
+(define (and x y)
+  (+ x y))
+
+(and 1 2)
+"""
+
+Which might seem cool, but I think the use cases are fairly weak.  Why would I want the 'and' macro(/special form - could
+be either, depending on the implementation) to replaced.  By a function, no less.
+
+And so applies, or function calls if you like, are, in my opinion, just another special form (here I am talking about
+eval/apply here, not the function).  The original McCarthy lisp implementation just arbitrarily gave it a special
+evaluation rule - outside the scope of all other evaluation rules, and it hasn't really been questioned.
+
+I believe all special forms (and macros) are just special evaluation rules for when that symbol of the first argument
+resolves to a special form.  I also believe if it isn't a macro or special form, it is a compile time error.
+
+So reiterate the obvious then, the basic rule of lisp is if it is not a special form, then it is a function call
+(apply), and now a runtime thing to figure what the hell it is.
+
+As an aside, I have read somewhere, that clojure claims having two brackets ie '((' in any form is very rare.  Writing
+something like this:
+(fn1 (fn2 1 2) 12)
+
+I have never, ever seen anyone write this, even though it is valid syntax.
+
+(fn1(fn2 1 2) 12)
+
+moving away from the lispy way
+------------------------------
+
+In mathmatics, functions are generally written as f(x).  It is natural on the eyes, and most imperative programming
+language do use this notation.  So why did lisp think it was so great to move away from it?  I am going to turn things
+on their head.  And yes I do consider evaluating lispy data structures is very cool, but let's just get away from the
+one special rule for function calls.
+
+Now we can't write:
+
+(fn 1 2 3) - we must write (callfun fn 1 2 3).  Not really a big deal right?  haha, I imagine everyone will stop reading
+at this point.
+
+Ok, so now let's just consider *adding* one rule to lisp's brackets rules, then we can see at a glance what is
+a special form or macro and what is an apply.  The rule is a whitespace thing, so warning to every lisp programmer in the world
+- be ready to baulk.  It is really just what I described.
+
+In short, this best shown by an example:
+
+What people are used to seeing:
+
+(prn 1 2 3)
+
+will now be written as
+
+prn(1 2 3)
+
+which both could resolve effectively to (callfunc prn 1 2 3), where callfunc is a special form to call a function with 1 2 3 arguments.
+
+
+More precisely, if there is any whitespace between prn and the brackets, it will be considered a special form/macro.
+If the special form/macro doesn't exist - or in other words *is not a special form* (because it would indeed resolve to
+a function, or whatever).  It is a compile time error.  This is compile time, and not runtime because all special forms
+and macros are, in fact, compile time things.
+
+The major change therefore is that there is no such thing anymore as a function being 'applied'.  Code will be
+transformed so that the we always end up with (funcall fn arg1 arg2).  It ends up unifiying all the evaluation rules in
+lisp, keeps things as a lisp-1 and makes the interpreter implementation much simpler.
+
+
+readers / macros / code transforms
+----------------------------------
+
+:Case 1:
+
+What happens if we write code like this:
+
+(fn 1 2 3)
+
+Our reader will just create the lisp data structures.
+
+Then we apply macro expansions.
+
+Then we perform a bunch of code transforms.  This is effectively the compiler stage, and is a little complex.
+
+It will then determine that this fn is *not* a special form, and raise an error.
+
+----
+
+:Case 2:
+
+What happens if we write code like this:
+
+(funcall fn 1 2 3)
+
+Our reader will just create the lisp data structures.
+
+Then we apply macro expansions.
+
+Then we perform a bunch of code transforms.  This is effectively the compiler stage, and is a little complex.
+
+It will then determine that funcall is a special form and we will end up with evaluating it, no problem.
+
+----
+
+:Case 3:
+
+What happens if we write code like this:
+
+fn(1 2 3)
+
+Our reader will treat this as a special case, create the following lisp data structure.
+
+(funcall fn 1 2 3)
+
+Which then is the same as case 2.
+
+
+
+What else?
+----------
+
+So from here we can open a nice beautiful can of worms.  Lets add the following rules:
+
+0. what we just talked about
+
+1. ',' (that is the character comma), is white space.  This is so in clojure too.
+
+So prn(1,2,3) is equivalent to prn(1 2 3)
+
+2. A ':' (that is the character colon) at the end of a line, indicates a python-esque whitespace rule, and the reader
+will effectively insert an implied wrapping set of brackets.
+
+So the cool thing in all this, we can keep the lispy syntax and ultimate evaluation of lisp data structures.  But now
+we have a reader that implements all this.  Macros don't die - which is at the end of the day lisp defining and salient
+feature (again IMO).
+
+So for example - here is an scheme like implementation of map
+
+(define map
+  (lambda (f l)
+          (cond
+           ((null? l) l)
+           (:else (cons (f (car l)) (map f (cdr l)))))))
+
+written with the above 3 rules.
+
+define map:
+  lambda (f, l):
+    cond:
+      null?(l) l
+      :else cons(f(car(l)), map(f, (cdr(l))))
+
+The reader well then redefine as:
+
+(define map
+  (lambda (f l)
+          (cond
+           ((funcall null? l) l)
+           (:else (funcall cons (funcall f (funcall car l)) (funcall map f (funcall cdr l)))))))
+
+Since apply is now a special form that takes the place of (fn args).
+
+Although ultimately I like to write like this
+
+defn map [f l]:
+    if null?(l) l cons(f(car(l)), map(f, (cdr(l))))
+
+or
+
+defn map [f l]:
+    if null?(l):
+        l
+        cons(f(car(l)), map(f, (cdr(l))))
+
+
+----
+
+* although this idea sounds good, I have no idea if there will probably be something that completely breaks it.  It is
+  just a thought experiment, which started as how can I tell whether something is a macro or function when reading other
+  people's code.
+
+* I still like to keep the scheme/clojure way of things - there is no reason to disallow valid code on this front.  We
+  should do like racket, which is include a top level prgama like "#lang scheme/base" or "#lang clojure/base
+  as to what language we are using.
+