6 ms·
Okay, I'll bite... 1. Recursion and cons cells naturally complement one another. They can stand in for n-ary tuples, vectors, and arrays reasonably well for sm
by rcoder 19y ago
Okay, I'll bite...
1. Recursion and cons cells naturally complement one another. They can stand in for n-ary tuples, vectors, and arrays reasonably well for small values of n, and when your dataset gets large, well, you switch to a more efficient data structure. Hell, with Arc you even get 'push' and 'pop' as built-in functions. What exactly is the problem?
2. Again, I don't see how this is a problem. Macros are compile-time rewriting rules. In an eagerly-evaluated language like Lisp, they are pretty much a necessary evil to prevent unnecessary side-effects while still allowing for flexible syntax. I can imagine doing without them in a lazy language like Haskell, but I'm not aware of any popular, lazy languages in the Lisp family.
That being said, I'm interested to know if you've working with other languages where #1 and #2 aren't a problem.
- ced 19y ago1. But what do you gain from having cons cells limited to 2 elements? I contrast cons cell list mostly to Python lists, which just seem to me to be a better abstraction... Do you think in terms of 2-element pairs or in term of lists? I guess in a way you are right, if the two are essentially equivalent, maybe there is no problem. 2. Macros come with a ton of pitfalls and limitations. On Lisp has a nice list of reasons to avoid macros. But consider if your macros are really called at run-time rather than compile-time, and you have a special operator like eval, but that evaluates in the calling context. Then you can write normal functions like this: (defmacro average (x y) (/ (+ (eval-in-caller x) (eval-in-caller y)) 2)) and (defmacro my-if (condition x y) (cond ((eval-in-caller condition) (eval-in-caller x)) (t (eval-in-caller y)))) defun can be written straightforwardly as a special case, and in fact extended so that eg.: (defun do-many-tymes (n-times ¬-evaluated body) ...) would behave half like a function and half like a macro. These run-time "macros" can be function-quoted, recursive, and have no problem with variable capture. With such a defun, you no longer need a defmacro. These are my fantasies about fixing stuff, so I won't waste anyone's time by writing more. I don't know if that could work. Macros are just so ugly... I was curious to see if PG or anyone had tried alternatives.
- shiro 19y agoI think they've been done and abandoned. You might want to dig some history to see why (I don't have references handy. Maybe asking comp.lang.lisp would help). (1) Let's separate implementation and semantics issues. In semantics level, list as a datatype is defined recursively: List a = () | (a, List a) This definition maps well to recursive algorithms. It also maps well to the implementation that uses cons cell, but as you say, it's not the only way to implement it. There was a technique called CDR-coding, which represents a list as if it's a vector (e.g. cell's CDR doesn't contain a pointer but it overlaps the next cell's CAR); specially tagged pointer distinguished normal cells and CDR-coded cells. I guess it was abandoned since deailng with set-cdr! would be very messy. But if you're creating a new dialect, you can go for big change like dropping set-cdr! (some of Scheme people even talking about dropping mutable cells.) In today's computer where access locality means a lot, maybe you can shed a new light to this old technique. (2) I think the current state of macro was the result of simplification. IIRC, the old lisp dialects allow to pass around syntactic values (FSUBRs) as if it's first class, but it's easily messes up the semantics. It's conceptually simpler to restrict macros as curretly they are. If passing macro around is allowed, you'll never know how (+ 1 2) in the following function is evaluated until runtime: (defun call-it (x) (funcall x (+ 1 2))) It may be evaluated as a normal expr, or in a completely different way, depending on whether x is a macro or not. Or are you suggesting that, in this case, (+ 1 2) is evaluated immediately because funcall is not a macro? It is a possible strategy, but then it clipples the expressive power, since it can't represent the same thing as: (defmacro call-it (x) `(,x (+ 1 2))) But there are still people who wants to unify macros and functions. Search comp.lang.lisp and comp.lang.scheme---I've seen some discussions there. Also search "first class macros"; there are some papers. Another way to eliminate (most) need of macros is to adopt normal order evaluation. I've seen some Lisp dialects using lazy evaluation by default. Beware that it changes the programming style a lot, though.
- ced 19y agoThanks a lot for the comments. You seem to know much more about these things than I do, so I don't have much to add. 2. call-it: What's the problem with not knowing (+ 1 2) until run-time? Efficiency? Then I would argue that expressive power of the core language ought to take precedence. In an actual implementation, funcall could leave (+ 1 2) to be evaluated (or not) inside the function body. I would add funcall-normal-function to the language, which would amount to a funcall + (declare (normal-function f)). Both for documentation and for efficiency. Of course, not knowing if (funcall x (print 2)) will print 2 is unsettling in a way, but that's the same basic argument that's made against macros. They disrupt the evaluation order. I read about normal-order before, but it didn't make much practical sense. I'll Google your pointers and look it up.