4 ms·
Hi Kazinator, Thank you for reply, I didn't remember our discussion and no, sadly, I didn't found how to implement lexical scoping until now. The workaround is
by martyalain 6y ago
Hi Kazinator,
Thank you for reply, I didn't remember our discussion and no, sadly, I didn't found how to implement lexical scoping until now. The workaround is a kind of "manual closure" as illustrated in
http://lambdaway.free.fr/lambdawalks/?view=closure http://lambdaway.free.fr/lambdawalks/?view=closure
http://lambdaway.free.fr/lambdawalks/?view=lambda http://lambdaway.free.fr/lambdawalks/?view=lambda
where is used the capability of lambdas to accept partial calls and IIFE build the bridge.
Time going on I improved lambdatalk, in a total loneliness. Except Ward Cunningham who finds some interest in a language which could replace in wikis the boring standard Markdown syntax.
I would like to know what you think of such an introduction
http://lambdaway.free.fr/lambdawalks/?view=fromroots2canopy http://lambdaway.free.fr/lambdawalks/?view=fromroots2canopy
and, for instance
http://lambdaway.free.fr/lambdawalks/?view=reduce http://lambdaway.free.fr/lambdawalks/?view=reduce
Thank you once more for your kind reply.
Alain Marty
- kazinator 6y agoI found some past discussions but not sure if that was the right one. Anyway, suppose we have a pencil-and-paper Lisp dialect, where everything is evaluated by rewriting objects on paper. We can have lexical scoping. That is to say, lexical scoping is achievable by substituting terms for variables. In particular, let us restrict to immutable variables. Like say we have (let ((x <whatever>) (y <whatever-else>)) (body x y)) Suppose that is just a picture: a character string or pencil marks on paper. We see what the scope is of the let and can basically just substitute: (body <whatever> <whatever-else>) It is the same with lambda. Say we have a lambda definition like (lambda (x y) (body x y)) When we call this with parameters <foo> and <bar>, whatever those things are, we substitute the arguments for x and y into the body: (body <foo> <bar>). This substitution can take place by a recursive walk of the code which respects scope, so for instance, if we have: (lambda (x y) (let ((x <xyzzy>)) (body x y))) the substitution will not just blindly replace the x as a character string. It walks the expression, and processes the (let ...) recursively, recognizing that x is shadowed by a new definition. The (let ...) part is recursively expanded and then substituted in place and so on. You need to pass down some environment object during the expansion. But: your lambdas do not have to have captured environments! They can be fully substituted. So that is to say: (let ((x <captured>)) (lambda (y) (+ x y))) can produce something like (lambda (y) (+ <captured> y)) no environment. The expansion/substitution process of (let ...) naturally took care of substituting the x value. The lambda parameter y remains unsubstituted: that happens at call time. The substitute function should probably be aware of lambda and keep track of the y, but specially annotated in the environment as "this is a lambda variable: do not substitute now". That sort of thing. My main point is that if you don't need mutable environments, then pencil-and-paper lexical scoping can be simulated in character strings by substitution. (Mutation throws a monkey wrench into things, like if you want to have stateful lambda procedures that return an incrementing counter whose initial value is a captured lexical.)
- martyalain 6y agoThank you for these explanations. As introduced in this page http://lambdaway.free.fr/lambdawalks/?view=lambda http://lambdaway.free.fr/lambdawalks/?view=lambda, in lambdatalk everything begins with such a text replacement process: replace :a0 :a1 ... an-1 in expression containing some occurences of :ai by v0 v1 ... vp-1 rewritten in a prefixed parenthesized form {{lambda {:a0 :a1 ... an-1} expression containing some occurences of :ai // it's a string } v0 v1 ... vp-1} It's nothing but an IIFE, (Immediately Invoked Function Expression). This is a simple example: 1) {{lambda {x y} {+ x y}} 3 4} -> 7 2) {{lambda {x y} {+ x y}} 3} -> {lambda {y} {+ 3 y}} 3) {{lambda {y} {+ 3 y}} 4} -> 7 In lambdatalk the implementation makes lambdas true "combinators", knowing nothing but global constants (primitives and user defined ones). No free variables, no closures, pure functions, entirely independent of any context. Pure functions without any side effect. True referential transparency. The lack of closures (I love them in Javascript) can fortunately be compensated by the fact that lambdas accept de facto "partial calls", and transfers of values can be done by "IIFE". Arrays are the only one mutable objects and I use them to avoid recursion's stackoverflow, for instance in this page http://lambdaway.free.fr/lambdawalks/?view=reduce http://lambdaway.free.fr/lambdawalks/?view=reduce
- kazinator 6y agoSo, I'm saying: that stuff could have closures, too.
- martyalain 6y agoI'am happy to know that that stuff can have closures but I'm not smart enough to implement them. I am a retired architect and not a professional coder. Meanwhile this is the workaround I use. For instance, the area of any triangle of sides[a,b,c] is given by the formula area = √s*(s-a)*(s-b)*(s-c) where s = (a+b+c)/2 If the intermediate variable s is provided alone in an IIFE, like this : {def area1 {lambda {:a :b :c} {{lambda {:s} // it's an IIFE where :s will be replaced ... {sqrt {* :s {- :s :a} {- :s :b} {- :s :c}}} // ... here ... } {/ {+ :a :b :c} 2}}}} // ... by this value computed once -> area1 {area1 3 4 5} -> NaN // Not a Number it doesn't work, simply because [:a :b :c] are not in the inner function's arguments list and a function knows nothing about its context. It is necessary to manually add the arguments of the calling function {def area2 {lambda {:a :b :c} {{lambda {:a :b :c :s} // :a :b :c are redefined ... {sqrt {* :s {- :s :a} {- :s :b} {- :s :c}}} } :a :b :c // ... get values from the outer func ... {/ {+ :a :b :c} 2}} // ... and from the new computed value }} -> area2 {area2 3 4 5} -> 6 For information lambdas are implemented at the beginning of this javascript file http://lambdaway.free.fr/lambdawalks/meca/JS.js http://lambdaway.free.fr/lambdawalks/meca/JS.js I wouldn't want to complicate the code too much and especially lose the automatic partial application, which seems to me incompatible with closures. Thank you for your kind remarks