2 ms·
Referential transparency is a big feature in that it hugely aids in reasoning about your program. I agree that it takes away the power of making your code hard
by tromp 2y ago
Referential transparency is a big feature in that it hugely aids in reasoning about your program. I agree that it takes away the power of making your code hard to reason about:-)
- kazinator 2y agoAll you need is compiler diagnostics about variable shadowing to make this academic issue entirely go away: 1> (defmacro mystery-macro (form) ^(let ((x 73)) ,form)) mystery-macro 2> (with-compile-opts () (compile-toplevel '(let ((x 42)) (mystery-macro x)))) ** expr-2:2: warning: let: variable x unused #<sys:vm-desc: a0db560> 3> (with-compile-opts (:warn shadow-var) (compile-toplevel '(let ((x 42)) (mystery-macro x)))) ** expr-3:2: warning: let: variable x shadows local variable ** expr-3:2: warning: let: variable x unused #<sys:vm-desc: a0fd0f0> 4> (with-compile-opts (:error shadow-var) (compile-toplevel '(let ((x 42)) (mystery-macro x)))) ** expr-4:2: let: variable x shadows local variable ** during evaluation of form (compile-toplevel '(let ((x 42)) (mystery-macro x))) ** ... an expansion of (progn (compile-toplevel '(let ((x 42)) (mystery-macro x)))) ** ... an expansion of (with-compile-opts (compile-toplevel '(let ((x 42)) (mystery-macro x)))) ** which is located at expr-4:1 Moreover, this is a problem that mainly affects densely nested code. If you write small functions that are not nested beyond a couple of lexical levels or so, referentially opaque smells have nowhere to hide. Example with macro generating code that unhygienically relies on list, which is redefined around the macro: 1> (defmacro mac (x) ^(list ,x)) mac 2> (with-compile-opts (:warn shadow-fun) (compile-toplevel '(labels ((list ())) (mac 1)))) ** expr-2:2: warning: sys:lbind: redefining list, which is a built-in defun ** expr-2:2: warning: sys:lbind: function list shadows global function ** expr-2:2: warning: list: too many arguments: max 0, given 1 #<sys:vm-desc: 9d0e4a0> 3> (with-compile-opts (:error shadow-fun) (compile-toplevel '(labels ((list ())) (mac 1)))) ** expr-3:2: warning: sys:lbind: redefining list, which is a built-in defun ** expr-3:2: sys:lbind: function list shadows global function ** during evaluation of form (compile-toplevel '(labels ((list ())) (mac 1))) ** ... an expansion of (progn (compile-toplevel '(labels ((list ())) (mac 1)))) ** ... an expansion of (with-compile-opts (compile-toplevel '(labels ((list ())) (mac 1)))) ** which is located at expr-3:1 The concept of referential transparency is meaningless if there is no shadowing. If an expression which refers to a certain binding is moved around in the program, it either continues to refer to that same binding, or at worst becomes a free reference (easily diagnosable). If there is no shadowing, constructs cannot surreptitiously intercept bindings.
- kazinator 2y agoAh right! The other aspect of referential transparency is the lack of side effects. If we can do x := x + 1, then the expression x isn't referentially transparent. If we relocate it from before the assignment to after, even though it refers to the same binding, it doesn't refer to the same value; the relocation doesn't changes its meaning. No imperative language can be referentially transparent, as such. But that's all the useful languages in which shit gets done.