Beautiful Racket: Why Racket? Why Lisp?
beautifulracket.com
beautifulracket.com
If a function can "work on" variables, where that means mutate their values, it is the mutation that isn't functional, not the scoping.
If one function can see another's internal variables, that's because that child function is enclosed in the black box; it is nested under the definition which makes those variables visible.
1) Scheme
(define cons (lambda (x y) (lambda (z) (z x y))))
(define car (lambda (z) (z (lambda (x y) x))))
(define cdr (lambda (z) (z (lambda (x y) y))))
(car (cons 12 34)) -> 12
(cdr (cons 12 34)) -> 34
2) Lambdatalk {def cons {lambda {:x :y :z} {:z :x :y}}}
{def car {lambda {:z} {:z {lambda {:x :y} :x}}}}
{def cdr {lambda {:z} {:z {lambda {:x :y} :y}}}}
{car {cons 12 34}} -> 12
{cdr {cons 12 34}} -> 34
More to see, for instance, in http://epsilonwiki.free.fr/lambdaway/?view=krakow .Anyway, you could have lexical scope in your text based substitution hack if you worked a little harder at it:
{lambda {:outer} {lambda {:inner :x} :outer :inner}}
Don't denounce lexical scope just because you don't have it working yet." {lambda {:outer} {lambda {:inner :x} :outer :inner}} " Yes it could be an interesting way. Following Peter Norvig, I have written my small lisp here http://epsilonwiki.free.fr/lambdaway/?view=lambdalisp coming with nested environments, closures, lexical scoping. But not partial application.
" Don't denounce lexical scope just because you don't have it working yet. " I denounce nothing, I do respect the way Lisp and its dialects have been implemented! But I think it's not forbidden to explore another implementation where partial application (with memoization) could replace closure, lexical scoping, nested environments. Before this exploration I thought such a language couldn't have any interest. Today I changed my point of vue.
Partial application doesn't really compensate for an identifier reference not being able to reach its apparent definition across a lambda boundary.
To an extent it does. Yes, story: "I'd like to pass Z as an argument here. Oops, Z is an outer variable outside the current lambda, so not reachable. But, aha, this is the last argument of the call I'm making, so just omit it. A function will then evaluate out of here and emerge as a value in the outer scope where Z is visible. There we can apply Z to that function and presto: effectively, we got Z as the last argument."
I get it, but it's a very limited workaround scenario. What if Z isn't the last argument; what if it is two scopes removed instead of just one, and so on.
Pretty much most functional languages that have partial application sugar are also lexically scoped, aren't they?
(You know, even Niklaus Wirth's Pascal has local functions that can see the local variables in their parent functions! When those functions are used as functional arguments, they can go downward only ("downward funargs"). So they aren't first class closures.)