Here are Some of my favorite examples of simple syntactic sugar.
for f(v1 from x1, ..., vn from xn) { body }
==>
f(lam(v1, ..., vn) { body }, x1, ..., xn)
if let p = x { y } else { z }
==>
match x {
p => y,
_ => z
}
fun f(v1, ..., vn) { body }
==>
rec f = lam(v1, ..., vn) { body }
do x ==> x
do x; xs ==> x >>= lam(v) { do xs }
do v <- x; xs ==> x >>= lam(v) { do xs }
x and y ==> if x then y else false
x or y ==> if x then true else y
x is y
==>
try { x }
then(x) { add-test-result(x == y) }
catch(e) { add-test-result(false) } <!-- -->
x raises e
==>
try { x }
then(x) { add-test-result(false) }
catch(err) { add-test-result(err == e) }
let v1 = x1, ..., vn = xn in body
==>
(lam(v1, ..., vn) { body })(x1, ..., xn)
cases(T) x
| C1(v11, ..., v1k) => x1
| ...
| Cn(vn1, ..., vnk) => xn
==>
x.matcher({
C1: lam(v11, ..., v1k) { x1 },
...,
Cn: lam(vn1, ..., vnk) { xn }
})
In a typed language, some sugars benefit from being able to take the type of one of their subexpressions:
let v1 = x1, ..., vn = xn in body
==>
(lam(v1 : typof(x1), ..., vn : typeof(xn)) { body })(x1, ..., xn)
Some sugars benefit from being able to get source-location information. For example:
debug(msg)
==>
print("On line " + str(SRCLOC.line-number) + ":");
print(msg)
More sugars to think about: var-lifting, letrec, cond, circular refs, return, ErrorState, sugars from Cyenne, Pyret.