For those of us who are unfamiliar with Lisps, can you expand on how they break referential transparency (and how Standard ML contrasts in that regard)?
Or at least not inherently, if by “lisp” one is primarily referring to s-expressions.
(a a)
calls a function 'a' on a variable 'a'. Lisp knows this because the first thing that comes after the left paren is a functionthis isn't just user facing - for example let* kind of depends on creating bindings up front so they work across clauses, and then mutating them afterwards
In what language? I just checked Elisp, SBCL, and Guile, and they all error out if you refer to a variable not previously defined by a left-to-right traversal of the varlist:
(let* ((a (+ b 1)) (b 1)) a)
Edit: This doesn't work either: (let* ((a (lambda () (+ b 1))) (b 1)) (funcall a)) ; (funcall a) -> (a) for Schemes (define-macro (let* bindings &rest body)
(if (null? bindings)
`(progn ,@body)
`(let (,(car bindings))
(let* ,(cdr bindings)
,@body)))) Signature
(letrec BINDERS &rest BODY)
Documentation
Bind variables according to BINDERS then eval BODY.
The value of the last form in BODY is returned.
Each element of BINDERS is a list (SYMBOL VALUEFORM) that binds
SYMBOL to the value of VALUEFORM.
The main difference between this macro and let/let* is that
all symbols are bound before any of the VALUEFORMs are evalled.https://www.lispworks.com/documentation/lw70/CLHS/Body/s_let...