Which is better, scope or the *let macros?

Whatever is on your mind, whether Lisp related or not.
Post Reply
Jasper
Posts: 209
Joined: Fri Oct 10, 2008 8:22 am
Location: Eindhoven, The Netherlands
Contact:

Which is better, scope or the *let macros?

Post by Jasper » Sat Mar 21, 2009 6:24 am

Ever since making the umac macro, i have been doubting whether scope or *let macros are better.(I express the doubt in my second post in link, lets not talk about the crazy(and silly) syntax here yet, I might start a thread dedicated to it.) The first one is more 'lispy', but i don't see any disadvantages to having scope, however, i do see advantages:
  • Causes less hooks.
  • 'Scope-transparent' macros can make variables for you.
  • People from other languages are more used to it.(Circumstancial, this one.)
Further, the compiler should be able to figure it out when the different variables depend on each other. A big disadvantage is that people might be attracted to more imperative code, however, this is their responsibility, imo.

Macros such as iterate and loop have some qualities of scopes.(Can you do flets macrolets with them?) Basically, with the *let's the variables/function/macros are contained inside the macro, while with scopes, they 'flow out' into the nearest scope. It is pretty close to equivalent, you only have to remember which macros are scope and which 'spills' their scope. It is best if at the base, there is only one macro that 'spills' scope, named something like transparent-progn.

Jasper
Posts: 209
Joined: Fri Oct 10, 2008 8:22 am
Location: Eindhoven, The Netherlands
Contact:

Re: Which is better, scope or the *let macros?

Post by Jasper » Sat Mar 21, 2009 7:22 am

Alright, i got this, now to get an iterate driver that makes functions.

Code: Select all

(require :iterate)

(defpackage #:scope
  (:use #:common-lisp #:iterate)
  (:export scope))

(in-package #:scope)

(defun curly-hook-reader (stream char)
  (declare (ignore char))
  (do ((i 0 (+ i 1))
       (ch (read-char stream nil #\}) (read-char stream nil #\}))
       (str "" (concatenate 'string str (list ch))))
      ((char= #\} ch)
       `(scope ,@(read-from-string (format nil "(~D)" str))))))

(set-macro-character #\{ #'curly-hook-reader)

(defmacro setf- (op var &rest rest)
  `(setf ,var (,op ,var ,@rest)))

(defun symbol-name-= (sym str)
  (when (symbolp sym)
    (string= (symbol-name sym) str)))

(defmacro scope (&rest body)
  "Special progn, scans for definitions first."
  (let ((iter (when (symbol-name-= (car body) "ITER")
		(setf- cdr body))))
    `(iter
      ,@body
      ,@(unless iter '((finish))))))
Note that the curly hooks work with lisp, but slime may misbehave with them. (They should always work with the REPL, but not always with C-M-x, try putting progn around for that.)

I might try make the silly syntax i mentioned earlier, just for the heck of it. Also, it might attract people with an allergy for parentheses, which then we may cure.

Jasper
Posts: 209
Joined: Fri Oct 10, 2008 8:22 am
Location: Eindhoven, The Netherlands
Contact:

Re: Which is better, scope or the *let macros?

Post by Jasper » Sat Mar 21, 2009 2:43 pm

Code: Select all

WARNING the following can cause serious eye or mental damage.












(defpackage #:unlisp-muhaha!
  (:use #:common-lisp #:scope)
  (:export enable-fancy disable-fancy scope-fancy
	   binary)
  (:documentation "Assortment of fancy notation."))

(in-package #:unlisp-muhaha!)

{ top
;;Switches to turn fancy stuff on/off.
;  "Plist showing what fancy stuff is enabled."
  *fancy* := '(= t := t :curly-hooks t :straight-hooks t)
  
;;Scope fancy stuff.

;  "Adds fancy operators to define variables/functions to scope."
  (macro scope-fancy (&body body)
   { top-level := (when (symbol-name-= (car body) "TOP")
		    (setf body (cdr body))
		    t)
     `(scope ,@(when top-level '(top))
	     ,@(reverse
		(do ((b body (cdr b))
		     (out nil out))
		    ((null b) out)
		  { (collect el) := (push el out)
		    (cond
		      ((null (cddr b))
		       (collect (car b)))
		      ((and (eql (cadr b) :=) (getf *fancy* :=))
		       (if (listp (car b))
			   (collect `(fun ,(caar b) (,@(cdar b))
					  ,(caddr b)))
			   (collect `(var ,(car b) ,(caddr b))))
		       (setf b (cddr b)))
		      ((and (symbol-name-= (cadr b) "=") (getf *fancy* '=))
		       (collect `(setf ,(car b) ,(caddr b)))
		       (setf b (cddr b)))
		      (t
		       (collect (car b))))
		  })))
    })

  (symbol-name-= a b) :=
    { (desym x) :=
           (cond
	     ((symbolp x) (symbol-name x))
	     ((stringp x) x)
	     (t nil))
      (string= (desym a) (desym b))
    }
  
  (in-string char str) :=
    (loop for ch across str when (char= ch char) return t)

  (space-around these-str str) :=
;  "Puts space round characters in these-str, so they are separate \
;characters."
     (if (null these-str) str
       (coerce 
	  (loop for ch across str
	     when (in-string ch these-str) collect #\Space
	     collect ch
	     when (in-string ch these-str) collect #\Space)
	  'string))

  (curly-hook-reader to-macro &optional (hook #\{) (unhook #\})
			                     space-around)
      :=
;  "Outputs a function that 'binds' the curly hooks to a macro.\
; (can also do other hooks with optional arguments.)"
    (lambda (stream char)
      "Binds curly hooks to some macro."
      (declare (ignore char))
      (do ((ch #\Space (read-char stream nil unhook))
	   (n 1 (cond ((char= ch unhook) [n-1])
		      ((char= ch hook)   [n+1])
		      (t                 n)))
	   (str "" (if (and (= n 1) (char= ch unhook)) str
		       (concatenate 'string str (list ch)))))
	  ((= n 0)
	   (progn (unread-char ch stream) ;TODO where did i lose it?
	     `(,to-macro ,@(read-from-string
			    (format nil "(~D)"
				    (space-around space-around str))))))))

;;Binary functions.

  *binary-funs* := '(* % / + -)
;  "Symbols of binary functions and their order.(Latter is critical!)
;Note that '=' is not defaultly provided. (And the = of scope-fancy will 
;never meet with the binary funs.)")

  (binary-fancy-fun code &optional (bin-funs *binary-funs*)) :=
;  "See the binary macro."
    (if (or (null bin-funs) (null code))
      (if (null (cdr code)) (car code)
        (error "Malformed binary notation! All values must be separated by\
 binary functions!"))
      { bin-name := (if (listp (car bin-funs))
			(caar bin-funs)
			(car bin-funs))
        bin-fun  := (if (listp (car bin-funs))
			(cadar bin-funs)
			(car bin-funs))
	(multiple-value-bind (pre-bf post-bf)
	    (do ((i 0 [i+1])
		 (c code (cdr c)))
		((or (null c) (symbol-name-= (car c) bin-name))
		 (values (subseq code 0 i) (cdr c))))
	  (if (null post-bf) ;Not this one.
	      (binary-fancy-fun code (cdr bin-funs))
	      `(,bin-fun ,(binary-fancy-fun pre-bf)
			 ,(binary-fancy-fun post-bf))))
      })

  (macro binary (&rest code)
    "Fancy binary notation. (NOTE: much more inefficient then it could be,\
  on plus side, all compile-time.)"
    (binary-fancy-fun code))

  *space-around* := "*%/+-"

;;Enabling and disabling fancy stuff.

  (enable-fancy &rest list) :=
;  "Disable unlisps fancy stuff."
    (dolist (el list)
      (setf (getf *fancy* el) t)
      (case el
	(:curly-hooks
	 (set-macro-character #\{ (curly-hook-reader 'scope-fancy)))
	(:straight-hooks #|}|#
	 (set-macro-character #\[ #|]|#
	   (curly-hook-reader 'binary #\[ #\] *space-around*)))))

  (disable-fancy &rest list) :=
;Enable unlisps fancy stuff.
    (dolist (el list)
      (setf (getf *fancy* el) nil)
      (case el
	(:curly-hooks
	 (set-macro-character #\{ nil)) #|}|#
	(:straight-hooks
	 (set-macro-character #\[ nil)))) #|]|#
}

(set-macro-character #\{ #|}|# (curly-hook-reader 'scope-fancy))

(set-macro-character #\[ #|]|#
  (curly-hook-reader 'binary #\[ #\] *space-around*))
It is common lisp with reader macros, i promise :)

Lol this notation is the worst idea i have had about lisp so far.. Maybe i should still add these ;) viewtopic.php?f=2&t=159

Jasper
Posts: 209
Joined: Fri Oct 10, 2008 8:22 am
Location: Eindhoven, The Netherlands
Contact:

Re: Which is better, scope or the *let macros?

Post by Jasper » Thu Mar 26, 2009 8:55 am

Ok, i went a little crazy in this thread. I put the stuff along with the umac thing. Including the crazy ugly code. Btw you can change how crazy it actually is. You can, for instance only turn on the binary notation activated with square brackets.(that doesn't need whitespace between operators.)

I figure that while a scope might be handy in some cases, the fact that not having one is unhandy might get you to look for better ways of coding things.

Anyway, making this was pretty trivial in retrospect. Nice to know how to use reader macros.

Edit: Found this this WITH macro Which does exactly what scope does, except with a more loop-like syntax. I like my approach better, though.(in cliki)

Post Reply