I've just posted some additional examples of sweet-expressions for even more 
Lisp dialects, at:
   http://www.dwheeler.com/readable/version02.html

So my examples now cover these dialects: Scheme, Common Lisp, Arc, ACL2, PVS, 
BitC, AutoCAD Lisp (AutoLisp), Emacs Lisp, SUO-KIF, Scheme Shell (Scsh), GCC 
Register Transfer Language (RTL), MiddleEndLispTranslator (MELT), and 
Satisfiability Modulo Theories Library (SMT-LIB). 

I think these samples from different dialects show that sweet-expressions are 
NOT
tied to any specific semantic.  Past "readable" Lisp notations (M-expressions, 
Logo,
etc.) all failed in part because they were too tied to a particular semantic.
Thus they fell apart once you started to use lists seriously (e.g., in macros).
By showing that samples in these other dialects can be reasonably
represented in sweet-expressions, I can be more confident that
sweet-expressions are useful while not tied to a particular semantic.

Below is a cut-and-paste for SUO-KIF, Scsh (Scheme Shell),
Paul Graham's Arc, RTL, MELT, and  Satisfiability Modulo Theories Library 
(SMT-LIB).
These are pretty different, and yet they SEEM to work well.
Comments welcome.  If the below is hard to read, look at the web page:
   http://www.dwheeler.com/readable/version02.html

--- David A. Wheeler

=============================================================

SUO-KIF

Standard Upper Ontology Knowledge Interchange Format (SUO-KIF) is a language 
designed for use in the authoring and interchange of knowledge, e.g., by the 
Suggested Upper Merged Ontology (SUMO).

Original Lisp

Here's the original Lisp of a trivial example, "All farmers like tractors":

(forall (?F ?T)
  (=>
    (and
       (instance ?F Farmer)
       (instance ?T Tractor))
    (likes ?F ?T)))

Infix non-default

Without using any infix operators, we can get a nicer result:

forall (?F ?T)
  =>
    and
       instance(?F Farmer)
       instance(?T Tractor)
    likes ?F ?T

However, both "and" and "=>" (implies) are traditionally used as infix 
operators, rather than prefix operators. Using {...}, we can use them in that 
traditional manner:

forall (?F ?T)
  {{instance(?F Farmer) and instance(?T Tractor)} => likes(?F ?T)}

or

forall (?F ?T)
  { {instance(?F Farmer) and instance(?T Tractor)}
    =>
    likes(?F ?T)}




Scsh (Scheme Shell)

Scheme shell is an implementation of Scheme, plus a high-level process notation 
for doing shell-script-like tasks (running programs, establishing pipelines, 
and I/O redirection).

Scsh's documentation (section 1.4) notes that scsh is (currently) "primarily 
designed for the writing of shell scripts - programming. It is not a very 
comfortable system for interactive command use: the current release lacks job 
control, command-line editing, a terse, convenient command syntax, and it does 
not read in an initialisation file analogous to .login or .profile." Most of 
these limitations can be easily fixed by reusing existing code; e.g., job 
control code can be copied from other shells, there are trivial libraries (such 
as readline) that implement command-line editing, and reading an initialization 
file is trivially done. But the "convenient command syntax" is a serious 
stumbling block to using scsh as a shell, and that is not trivially fixed by 
simply reusing arbitrary code. It can be fixed by using sweet-expressions.
Original Lisp

In traditional shells, you might write a command like this:

gunzip < paper.tex.gz | detex | spell | lpr -Ppulp &

In scsh, this could be written as:

(& (| (gunzip) (detex) (spell) (lpr -Ppulp)) ; background a pipeline 
    (< paper.tex.gz))                        ; with this redirection

Infix non-default

In sweet-expressions, the same expression of scsh can be written as:

&
  | gunzip() detex() spell() lpr(-Ppulp) ; background a pipeline 
  < paper.tex.gz                         ; with this redirection

or as:

&
  {gunzip() | detex() | spell() | lpr(-Ppulp)} ; background a pipeline 
  < paper.tex.gz                               ; with this redirection

I think it's better if punctuation-only names are used primarily for infix 
operators; it helps with consistency. At the least, I'd rename "&" into the 
synonym "background". Then it becomes:

background
  {gunzip() | detex() | spell() | lpr(-Ppulp)} ; background a pipeline 
  < paper.tex.gz                               ; with this redirection




Arc

Paul Graham is developing a new dialect of Lisp named Arc; Paul Graham's Arc 
site and Arclanguage.org have more information. Semantically, it's a lot like a 
special combination of Common Lisp and Scheme. For example, like Common Lisp, 
both "false" and "empty list" are nil (Scheme has separate objects for false 
and the empty list). But like Scheme, Arc has a single namespace (it's a 
Lisp-1), as compared with Common Lisp (which is a Lisp-2). Arc has some cool 
ideas, but Paul Graham seems to be very busy on other things, so an informal 
community called 'Anarki' has formed to evolve Arc. (It uses "=" for 
assignment, which I think is unfortunate because "=" is often confused with 
is-equal-to, but anyway...). A good place to start is the Arc tutorial.

In Arc, [...] has a special meaning. More specifically, "[... _ ...]" is an 
abbreviation for "(fn (_) (... _ ...))". For example:

 arc> (map [+ _ 10] '(1 2 3))
 (11 12 13)

This makes me think that sweet-expressions should not mandate that [...] be 
equivalent to (...), necessarily, in sweet-expressions. Instead, the meaning of 
[...] may be somewhat language-dependent in sweet-expressions; a Scheme using 
sweet-expressions might consider [...] equivalent to (...), since the normal 
Scheme R6 does, but it doesn't necessarily follow that this must be true for 
all languages. If you disable sweet-expression's definition that [...] is the 
same as (...), it looks like Arc and sweet-expressions could be easily used 
together.

Original Lisp
The Arc tutorial has several really tiny examples; let's pick out a few and put 
them in one place:

(+ (+ 1 2) (+ 3 (+ 4 5)))
(def average (x y) 
       (/ (+ x y) 2))
(let x 10
  (while (> x 5)
    (= x (- x 1))
    (pr x)))
(mac repeat (n . body)
  `(for ,(uniq) 1 ,n ,@body))
(def firstn (n xs)
  (if (and (> n 0) xs)
      (cons (car xs) (firstn (- n 1) (cdr xs)))
      nil))
(def nthcdr (n xs)    
  (if (> n 0)
      (nthcdr (- n 1) (cdr xs))
      xs))  
(def tuples (xs (o n 2))
  (if (no xs)
      nil
      (cons (firstn n xs)
            (tuples (nthcdr n xs) n))))

(def mylen (xs)
      (if (no xs)
          0
          (+ 1 (mylen (cdr xs)))))

(mac n-of (n expr)
  (w/uniq ga
    `(let ,ga nil
       (repeat ,n (push ,expr ,ga))
       (rev ,ga))))

Infix non-default
Here are the sweet-expression equivalents:

{{1 + 2} + {3 + {4 + 5}}}
def average (x y) 
       {{x + y} / 2}
let x 10
  while {x > 5}
    {x = {x - 1}}
    pr x
mac repeat (n . body)
  `for ,uniq() 1 ,n ,@body
def firstn (n xs)
  if {{n > 0} and xs}
      cons car(xs) firstn({n - 1} cdr(xs))
      nil
def nthcdr (n xs)    
  if {n > 0}
      nthcdr {n - 1} cdr(xs)
      xs
def tuples (xs (o n 2))
  if no(xs)
      nil
      cons firstn(n xs)
           tuples nthcdr(n xs) n

def mylen (xs)
      if no(xs)
          0
          {1 + mylen(cdr(xs))}

mac n-of (n expr)
  w/uniq ga
    `let ,ga nil
       repeat ,n push(,expr ,ga)
       rev ,ga

Note the last macro; even though it's short, the original required 4 closing 
parentheses to complete it, while the sweet-expression required none.




RTL (GCC Register Transfer Language)

The GNU Compiler Collection (GCC) has an internal structure for representing 
programs (at a low level), the GCC Register Transfer Language (RTL). It's often 
useful to print out RTL; it's useful for debugging, and other tools use it too 
(such as RTL-check and Egypt. Here's a summary of some RTL semantics. RTL has a 
Lisp-like external representation, which is why it's noted here.

Original Lisp

"Compilation of Functional Programming Languages using GCC - Tail Calls" by 
Andreas Bauer, has some RTL examples.

Section 2.3 shows a trivial C program:

  int foo ()
  {
     return bar (5);
  }

Here is an incomplete translation to RTL expressions (before any sibling call 
optimization):

 (call_insn 19 9 20 (nil) (call_placeholder 16 10 0 0
     (call_insn 17 16 18 (nil)
     (set (reg:SI 0 eax)
              (call (mem:QI (symbol_ref:SI ("bar")) [0 S1 A8])
                      (const_int 4 [0x4]))) -1 (nil)
          (expr_list:REG_EH_REGION (const_int 0 [0x0])
               (nil))
          (nil))) -1 (nil)
     (nil)
     (nil))

 (insn 20 19 21 (nil) (set (reg:SI 58)
          (reg:SI 59)) -1 (nil)
     (nil))

 (jump_insn 21 20 22 (nil) (set (pc)
          (label_ref 25)) -1 (nil)
     (nil))

 (barrier 22 21 23)

 (note 23 22 27 NOTE_INSN_FUNCTION_END)

 (insn 27 23 28 (nil) (clobber (reg/i:SI 0 eax)) -1 (nil)
     (nil))

 (insn 28 27 25 (nil) (clobber (reg:SI 58)) -1 (nil)
     (nil))

 (code_label 25 28 26 6 "" [0 uses])

 (insn 26 25 29 (nil) (set (reg/i:SI 0 eax)
         (reg:SI 58)) -1 (nil)
     (nil))

 (insn 29 26 0 (nil) (use (reg/i:SI 0 eax)) -1 (nil)
     (nil))

Infix non-default
Below is a sweet-expression equivalent. One question: Should lists here be 
represented as x(...) or (x ...)? They are equivalent; it's merely a matter of 
what is clearer. The first parameter is a special marker, so it's reasonable to 
represent them as x(...); besides, it helps to show off sweet-expressions. I 
won't do that with strings; a list beginning with a string will be shown as 
("x"). RTL isn't really typical Lisp, but has its own syntactic extensions. 
I'll show RTL bracketed expressions [...] exactly as they would be in RTL 
(leaving them as-is). So here's one approach to representing RTL:

 call_insn 19 9 20 nil()
   call_placeholder 16 10 0 0
     call_insn 17 16 18 nil()
      set reg:SI(0 eax)
        call(mem:QI(symbol_ref:SI(("bar")) [0 S1 A8]) const_int(4 [0x4]))
      -1
      nil()
      expr_list:REG_EH_REGION const_int(0 [0x0]) nil()
      nil()
   -1
   nil()
   nil()
   nil()

 insn 20 19 21 nil() set(reg:SI(58) reg:SI(59)) -1 nil() nil()

 jump_insn 21 20 22 nil() (set pc() (label_ref 25)) -1 nil() nil()

 barrier 22 21 23

 note 23 22 27 NOTE_INSN_FUNCTION_END

 insn 27 23 28 nil() clobber(reg/i:SI(0 eax)) -1 nil() nil()

 insn 28 27 25 nil() clobber(reg:SI(58)) -1 nil() nil()

 code_label 25 28 26 6 "" [0 uses]

 insn 26 25 29 nil() set(reg/i:SI(0 eax) reg:SI(58)) -1 nil() nil()

 insn 29 26 0 nil() use(reg/i:SI(0 eax)) -1 nil() nil()





MELT (MiddleEndLispTranslator)

MELT (MiddleEndLispTranslator) is a "high-level Lisp-like language designed to 
fit very closely in the [GNU Compile Collection (GCC)] internal 
representations, thru an automated translation of MELT code into GCC specific C 
code, compiled by a C compiler (usually some GCC) and then loaded as plugins. 
This enables easier development of high-level static analysis and 
transformation, working on GCC middle end representation (GIMPLE tuple)." If 
you're interested in the general topic of gcc plug-ins, see "Plugging into GCC" 
(lwn.net).

The MELT work is partly funded by the French Ministery of Economy, thru ITEA 
within the GlobalGCC (GGCC) project. The lead, Basile Starynkevitch, has 
contributed his GCC contributions using a copyright transfer signed by CEA to 
FSF. It was presented at the GCC Summit 2007. A special MELT branch was created 
on February 19, 2008.

Semantically, MELT is a "Lisp1" Lisp dialect (so it's more like Scheme than 
like Common Lisp regarding names and bindings). You can define primitives, 
which get translated to C, compiled, and linked into the compiler during the 
compilation process. The (unboxed) integer addition is pre-defined in MELT as:

  (defprimitive +i (:long a b) :long "((" a ") + (" b "))")

Where "the first :long occurrence describes the types of the formal arguments a 
and b, the second occurrence describes the result. There is an minimal object 
system (single-inheritance hierarchy, rooted at CLASS_ROOT... Tail-recursion is 
not handled (looping should use the forever keyword, and loops are can be 
exited)".

Original Lisp
Here is an example from the main MELT page. This is "a sample MELT function 
repeat-times [that applies] a function f to an argument x some specified n 
times. f and x are values, n is an unboxed long argument.":

(defun repeat-times (f x :long n) ; n is an unboxed formal argument of type long
  (forever reploop                ; infinite loop called reploop
     (if (<=i n 0)                ; test if the unboxed n is negative
         (exit reploop))          ; exit the loop if yes
     (f x)                        ; call f
     (setq n (-i n 1))))          ; decrement n

Infix non-default
Here is a sweet-expression equivalent:

defun repeat-times (f x :long n) ; n is an unboxed formal argument of type long
  forever reploop                ; infinite loop called reploop
    if {n <=i 0}                 ; test if the unboxed n is negative
      exit reploop               ; exit the loop if yes
    f x                          ; call f
    setq n -i(n 1)               ; decrement n

This is an interesting case regarding "infix default" vs. "infix non-default". 
The comparison operator here is "<=i" - and as a result, the comparison 
wouldn't be automatically detected by the proposed automatic infix detection 
rules. This is another argument for not trying to automatically detect infix 
operators; too often they will be missed, so don't bother. Instead, let the 
developer specify them using a single uniform syntax.




Satisfiability Modulo Theories Library (SMT-LIB)

The Satisfiability Modulo Theories Library (SMT-LIB) The major goal of the 
SMT-LIB initiative is to "establish a library of benchmarks for Satisfiability 
Modulo Theories, that is, satisfiability of formulas with respect to background 
theories for which specialized decision procedures exist... [these] have 
applications in formal verification, compiler optimization, and scheduling, 
among others... the initiative first aims at establishing a common standard for 
the specification of benchmarks and of background theories."

The SMT-LIB syntax is "attribute-based and Lisp-like". It permits a syntactic 
category "user value", used for user-defined annotations or attributes, that 
start with an open brace "{" and end with a closed brace "}".

Original Lisp
The QF_RDL benchmarks's "check" subset includes this small benchmark as 
bignum_rd1.smt. I've re-indented the last lines slightly so that it fits inside 
the usual 80 columns:

(benchmark bignum
  :source { SMT-COMP'06 Organizers }
  :notes "This benchmark is designed to check if the DP supports bignumbers."
  :status sat
    :difficulty { 0 }
    :category { check }
  :logic QF_RDL
  :extrafuns ((x1 Real))
    :extrafuns ((x2 Real))
    :extrafuns ((x3 Real))
    :extrafuns ((x4 Real))
  :formula
  (and (<= (- x1 x2) (/ 1 1000000000000000000000000000000000))
       (<= (- x2 x3) (/ 1 2000000000000000000000000000000011))
       (<= (- x3 x4) (~ (/ 1 1000000000000000000000000000000000)))
       (<= (- x4 x1) (~ (/ 1 2000000000000000000000000000000012)))))

The indentation above is misleading, since "difficulty" isn't a part of 
":status". So let's first use a reasonable indentation, trying to make it as 
readable as possible without changing Lisp syntax. Also, "user values" are 
written with {...}, which isn't standard Lisp; let's rewrite them as strings 
("...."), since they have essentially the same role (they are uninterpreted 
data):

(benchmark bignum
  :source "SMT-COMP'06 Organizers"
  :notes "This benchmark is designed to check if the DP supports bignumbers."
  :status sat
  :difficulty "0"
  :category "check"
  :logic QF_RDL
  :extrafuns ((x1 Real))
  :extrafuns ((x2 Real))
  :extrafuns ((x3 Real))
  :extrafuns ((x4 Real))
  :formula
  (and (<= (- x1 x2) (/ 1 1000000000000000000000000000000000))
       (<= (- x2 x3) (/ 1 2000000000000000000000000000000011))
       (<= (- x3 x4) (~ (/ 1 1000000000000000000000000000000000)))
       (<= (- x4 x1) (~ (/ 1 2000000000000000000000000000000012)))))

Infix non-default

One problem with this data is that there is an implied syntax that isn't 
actually embedded in the Lisp formatting. Namely, an atom beginning with ":" is 
followed by a parameter in this syntax. A pretty-printer that wasn't specially 
rigged with this convention would not show this format in a pretty way, and a 
normal sweet-expression reader wouldn't know about this either. This means that 
the "obvious" sweet-expression notation isn't right. E.G., this fragment:

benchmark bignum
  :source "SMT-COMP'06 Organizers"
  :notes "This benchmark is designed to check if the DP supports bignumbers."
  :status sat

Would be interpreted as: (benchmark bignum (:source "SMT-COMP'06 Organizers") 
(:notes "This benchmark is designed to check if the DP supports bignumbers.") 
(:status sat))

We could use this kind of formatting, but I find it ugly and counter-intuitive; 
it also wastes lots of space:

benchmark bignum
  :source
  "SMT-COMP'06 Organizers"
  :notes
  "This benchmark is designed to check if the DP supports bignumbers."
  :status
  sat

But this isn't really a problem. One way to make this "pretty" would be to use 
"(...)" at an outer level - such as with function-calling syntax - which 
disables indentation processing. Then, we can choose any indentation we like, 
just as we can in traditional Lisp syntax. We can still use {...} for infix 
operators. In our examples, I won't use "and" as an infix operator, because the 
sub-expressions are so long, but I will use infix for "<=", "-", and "/". I'll 
also use "~" (unary minus) as a one-parameter function.

benchmark( bignum
  :source "SMT-COMP'06 Organizers"
  :notes "This benchmark is designed to check if the DP supports bignumbers."
  :status sat
  :difficulty "0"
  :category "check"
  :logic QF_RDL
  :extrafuns ((x1 Real))
  :extrafuns ((x2 Real))
  :extrafuns ((x3 Real))
  :extrafuns ((x4 Real))
  :formula
    and  { {x1 - x2} <= {1 / 1000000000000000000000000000000000} }
         { {x2 - x3} <= {1 / 2000000000000000000000000000000011} }
         { {x3 - x4} <= ~({1 / 1000000000000000000000000000000000}) }
         { {x4 - x1} <= ~({1 / 2000000000000000000000000000000012})} )

The above looks reasonable enough. But if you were able to change the required 
expressions, you could make the structure of the parameters much clearer by 
connecting the parameter names and their values inside lists, instead of merely 
implying it through a naming convention. This would be an annoyance in 
traditional Lisp, because it'd require additional parentheses for each 
parameter, but this is a non-problem for sweet-expressions. If this was done, 
you could write it this way:

benchmark bignum
  :source "SMT-COMP'06 Organizers"
  :notes "This benchmark is designed to check if the DP supports bignumbers."
  :status sat
  :difficulty "0"
  :category "check"
  :logic QF_RDL
  :extrafuns ((x1 Real))
  :extrafuns ((x2 Real))
  :extrafuns ((x3 Real))
  :extrafuns ((x4 Real))
  :formula
    and  { {x1 - x2} <= {1 / 1000000000000000000000000000000000} }
         { {x2 - x3} <= {1 / 2000000000000000000000000000000011} }
         { {x3 - x4} <= ~({1 / 1000000000000000000000000000000000}) }
         { {x4 - x1} <= ~({1 / 2000000000000000000000000000000012})}


--- David A. Wheeler 

-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
_______________________________________________
Readable-discuss mailing list
Readable-discuss@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/readable-discuss

Reply via email to