4 ms·
Beautiful Racket: Why Racket? Why Lisp?
- martyalain 10y agoI did appreciate your original 2014 version and I still love the way you speak of Lisps. About functional programming you write "Functional programming refers to a stricter style where functions receive certain data as input, process only that data, and return a result." and it's a common way of defining it. But most languages - and Lisps too - comes with closures and lexical scoping and (IMHO) that breaks the functional purity. Thanks to a closure an inner function can work on variables defined in the outer function, free variables can be defined and used out of the arguments list. It's nothing but side effects, the function is not isolated in a black box. Maybe you can think that the lack of closure is a strong weakness of a language. It is and it is not! You can see what can be done without closures in the [lambdaway project](http://epsilonwiki.free.fr/lambdaway/ http://epsilonwiki.free.fr/lambdaway/). And so with a true functional language. Your opinion is welcome.
- kazinator 10y agoClosures and lexical scoping are not side effects and do not break functional purity. 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.
- martyalain 10y agoThank you for these precisions. You say that "it is the mutation that isn't functional, not the scoping" and I agree with that, at least the inner function must not mutate the outer variables in order to respect the pure functional paradigm. But what about lexical scoping? The behaviour of the inner function depends on its environment and the inner function is not what I would call a black box. A function containing free variables, out of the arguments' list, is not a pure function, a bijection between inputs and outputs. Lexical scoping is useful - as are side effects - but it does introduce some bias in coding. What I wanted to think about is that a more pure functional approach can be reached replacing closure and lexical scoping by the partial application capability: a function called with a number of values less than its arity will store the given values and return a function waiting for the rest. Everything is done inside the function. Let's compare how [cons, car, cdr] are built in Scheme, with closure, and lambdatalk, with partial application: 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 http://epsilonwiki.free.fr/lambdaway/?view=krakow .
- kazinator 10y agoThat isn't even close to how cons, car and cdr are actually built in Scheme. (How would you add the "pair?" function to the above? Or stop any old one-argument lambda from being passed into car?) 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.
- martyalain 10y ago" That isn't even close to how cons, car and cdr are actually built in Scheme. " I know that, I have read SICP and some others, it's not my question. " {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 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.
- kazinator 10y agoPartial application isn't in conflict with closure. 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.)