In Arc, symbols that contain socalled ssyntax characters stand as abbreviations for expressions. You can see unabbreviated forms with ssexpand. arc> (ssexpand 'f.x)
(f x)
arc> (ssexpand 'f!x)
(f (quote x))
arc> (ssexpand '.x)
(get x)
arc> (ssexpand '!x)
(get (quote x))
arc> (ssexpand '~f)
(complement f)
arc> (ssexpand 'f:g)
(compose f g)
arc> (ssexpand 'f&g)
(andf f g)
Ssyntax can be combined according to (somewhat fragile) precedence rules, further abbreviating code. arc> (ssexpand 'f.x!y)
((f x) (quote y))
arc> (ssexpand '~f:g)
(compose (complement f) g)
arc> (ssexpand 'f&g:h)
(compose f&g h)
These expansions can be expanded even more since, e.g., compose and complement are macros. Indeed, ac.scm optimizes some away: ((compose f g) x) ; ==> (f (g x))
((complement f) x) ; ==> (no (f x))
But there are some places ssyntax never expands. arc> (let (x y) '(1 2) (+ x y))
3
arc> (let x.y '(1 2) (+ x y))
Error: "reference to undefined identifier: _x"
I usually find myself writing the expanded expressions instead. So I got curious: how much of my code could use ssyntax? Despite the expansion tool, I had to look manually for opportunities to contract. "Deoptimizing" can be nonobvious, precedence rules make it harder to combine ssyntax properly, and sometimes it just won't work.But hey, that's what computers are for. So, I wrote sscontract. arc> (sscontract '(f x))
f.x
arc> (sscontract '(f 'x))
f!x
arc> (sscontract '(get x))
.x
arc> (sscontract '(get 'x))
!x
arc> (sscontract '(complement f))
~f
arc> (sscontract '(compose f g))
f:g
arc> (sscontract '(andf f g))
f&g
Precedence rules? arc> (sscontract '(compose (andf f g) h))
f&g:h
arc> (sscontract '(andf f (compose g h)))
(andf f g:h)
arc> (sscontract '(compose f ((x y) z)))
f:x.y.z
arc> (sscontract '(andf f ((x y) z)))
(andf f x.y.z)
Check.Optimizations? arc> (sscontract '(and ((andf f g) x) (a x) (b x)))
f&g&a&b.x
arc> (sscontract '(no (f (g (h x)))))
(~f:g:h x)
arc> (sscontract '(map [_ 2] ((h 0) 0)))
(map !2 h.0.0)
Check.Special cases? arc> (sscontract '(assign (car a) b))
(assign (car a) b)
arc> (sscontract '(= (car a) b))
(= car.a b)
arc> (sscontract '(def f (x y) (x y)))
(def f (x y) x.y)
arc> (sscontract '(with ((x y) (f 5) z (g 10)) (+ x y z)))
(with ((x y) f.5 z g.10) (+ x y z))
Check.Simplifies code? ; Before
(def all (test seq)
(~some (complement (testify test)) seq))
; After
(def all (test seq)
(~some ~testify.test seq))
; Before
(def pair (xs (o f list))
(if (no xs)
nil
(no (cdr xs))
(list (list (car xs)))
(cons (f (car xs) (cadr xs))
(pair (cddr xs) f))))
; After
(def pair (xs (o f list))
(if no.xs
nil
(~cdr xs)
(list:list:car xs)
(cons (f car.xs cadr.xs)
(pair cddr.xs f))))
; Before
(def parseformat (str)
(accum a
(with (chars nil i 1)
(w/instring s str
(whilet c (readc s)
(case c
#\# (do (a (coerce (rev chars) 'string))
(wipe chars)
(a (read s)))
#\~ (do (a (coerce (rev chars) 'string))
(wipe chars)
(readc s)
(a (list argsym (++ i))))
(push c chars))))
(when chars
(a (coerce (rev chars) 'string))))))
; After
(def parseformat (str)
(accum a
(with (chars nil i 1)
(w/instring s str
(whilet c readc.s
(case c
#\# (do (a:coerce rev.chars 'string)
wipe.chars
(a:read s))
#\~ (do (a:coerce rev.chars 'string)
wipe.chars
readc.s
(a:list argsym ++.i))
(push c chars))))
(when chars
(a:coerce rev.chars 'string)))))
Well, it's for you to decide. If you don't like a contraction, you shouldn't use it. The aim is to produce a minimal (which is different from minimum) abbreviation of your code, erring towards correctness. This makes most macros' contractions boring, since quasiquote should mostly be left alone: arc> (sscontract '`(f x))
(quasiquote (f x)) ; since `f.x != `(f x)
arc> (sscontract '`(do ,(f x)))
(quasiquote (do (unquote f.x))) ; since `(do ,f.x) == `(do ,(f x))
but maybe it's okay in your code to contract certain quasiquoted items. Or perhaps ssyntax makes the code less readable. It depends.Still, it's useful to see how far ssyntax can go. http://bitbucket.org/fallintothis/contract/ NOTE: After you put contract/ in your Arc directory, you'll want to $ mv ./contract/sscontract.arc .
Then just arc> (load "sscontract.arc")
nil
arc> (sscontract '(hello world))
hello.world
