Subject: Re: The Parentheses Myth (was Re: new free web book on Common Lisp)
From: Erik Naggum <erik@naggum.net>
Date: Sat, 01 Jun 2002 13:07:38 GMT
Newsgroups: comp.lang.lisp
Message-ID: <3231925655526163@naggum.net>

* David Golden
| I sometimes wonder if one merely changed that opening-paren-position bit
| of lisp syntax, without worrying about infix v.s. prefix, would thousands
| of c programmers suddenly "get" lisp... (It seems to work quite well for
| what a c programmer would think of as programs, but what a c programmer
| would think of as data structures look a bit funny...)

  This is in fact quite easy to automate.  To give the feel for this way of
  writing the code, I have taken the liberty to convert a roman-numeral
  parser to the new style.  Please note that this was made a lot easier
  because I now write my code in Common Salt syntax instead of just Common
  Lisp.  In Common Salt, <> and () are both list operators, but <> hold an
  evaluatable form (or an XML-style "element"), whereas () simply hold a
  list as data.  (This is at one level mere syntax -- the point is to
  support different "readers" in each element; the "readers" of macros and
  special operators know how to parse the rest of their input, but if you
  ask for it, you get more information.)  E.g.,

<defun parse-roman-digit-sequence (count roman start end digit unit half cont fail)
  <cond (<^< count 0> <funcall fail nil>)
	(<= start end> 0)
	(<equalp <char roman start> <second unit>>
	 <+ <first digit>
	    <parse-roman-digit-sequence
	     <1- count> roman <1+ start> end digit unit half cont fail>>)
	(t
	 <funcall cont
		  roman start end <cdr digit> <cdr unit> <cdr half> fail>)>>

  Why Common Salt?  Common Salt is NaCl, the Next Advancement in Common Lisp.
  ^ has the same function as \ in Common Lisp.  \ got in the way too much.

  Now, this can easily be turned into the not-quite-prefix syntax:

in-package (:cl-user)

defun (parse-roman-digit-sequence, (count, roman, start, end, digit, unit, half, cont, fail),
  cond ((< (count, 0), funcall (fail, nil)),
	(= (start, end), 0),
	(equalp (char (roman, start), second (unit))
	 + (first (digit),
	    parse-roman-digit-sequence
	     (1- (count), roman, 1+ (start), end, digit, unit, half, cont, fail))),
	(t
	 funcall (cont,
		  roman, start, end, cdr (digit), cdr (unit), cdr (half), fail))))

defun (parse-old-roman-digit, (roman, start, end, digit, unit, half, fail),
  cond ((= (start end) 0),
	(null (digit), funcall (fail, nil)),
	(equalp (char (roman, start), first (half))
	 + (* (first (digit), 5),
	    parse-old-roman-digit
	     (roman, 1+ (start), end, digit, unit, cons (nil, cdr (half)), fail))),
	(t
	 parse-roman-digit-sequence
	  (4, roman, start, end, digit, unit, half, #'parse-old-roman-digit, fail))))

defun (parse-new-roman-digit, (roman, start, end, digit, unit, half, fail),
  cond ((= (start, end) 0),
	(null (digit), funcall (fail, nil)),
	(and (< (1+ (start), end),
	      equalp (char (roman, start), second (unit)),
	      equalp (char (roman, 1+ (start)), first (unit)))
	 + (* (first (digit), 9),
	    parse-new-roman-digit
	     (roman, + (2, start), end, cdr (digit), cdr (unit), cdr (half), fail))),
	(and (< (1+ (start), end),
	      equalp (char (roman, start), second (unit)),
	      equalp (char (roman, 1+ (start)), first (half)))
	 + (* (first (digit), 4),
	    parse-new-roman-digit
	     (roman, + (2, start), end, cdr (digit), cdr (unit), cdr (half), fail))),
	(equalp (char (roman, start), first (half)),
	 + (* (first (digit), 5),
	    parse-new-roman-digit
	     (roman, 1+ (start), end, digit, cons (nil, cdr (unit)), cons (nil, cdr (half)), fail))),
	(t
	 parse-roman-digit-sequence
	  (3, roman, start, end, digit, unit, half, #'parse-new-roman-digit, fail))))

defun (parse-roman-integer, (roman, &key, (start, 0), end, old-style),
  "Return the integer represented by the valid roman numeral, or nil.
Old-style uses four consecutive digits of a unit, while new-style
uses the next smaller unit before the unit or half-unit.",
  setq (end, or (end, length (roman))),
  when (< (-1, start, end, 1+ (length (roman))),
    flet (((parse-failure, (value),
	     return-from (parse-roman-integer, value))),
      funcall (if (old-style, #'parse-old-roman-digit, #'parse-new-roman-digit),
	       roman, start, end, '(1000, 100, 10, 1), '(nil, #\M, #\C, #\X, #\I), '(nil, #\D, #\L, #\V),
	       #'parse-failure))))

  I sort of feel like I have just re-invented Dylan, but I mostly hope
  people are _really_ scared.  (There are probably mistakes in the above.
  If you go look for them, you prove several interesting things about your
  psychology and realize that posting about it is yet another mistake.)
-- 
  In a fight against something, the fight has value, victory has none.
  In a fight for something, the fight is a loss, victory merely relief.

  70 percent of American adults do not understand the scientific process.