Hacker News new | ask | show | jobs
by jrapdx 4896 days ago
As presented, the example macro doesn't demonstrate referential transparency, but then again, the example appears to be incorrect.

The issue is invoking "(swapargs (mod 7 5))". I tried this in the Scheme REPL (Chicken to be precise), in which the macro was defined:

  (define-syntax swapargs
    (syntax-rules ()
      ((_ ls) (list (list-ref ls 0) (list-ref ls 2)
                    (list-ref ls 1)))))

  (swapargs '(a b c)) => (a c b)
  (swapargs (swapargs '(a b c))) => (a b c)
In other words, the macro does show referential transparency.

However, the following doesn't work:

  (swapargs (modulo 7 5)) => Error: (list-tail) bad   
                             argument type: 2
The problem is the argument is evaluated first, and "2" is not a list. (The macro requires a list-of-3 argument.)

  (modulo 7 5) => 2
Probably, what was intended was like this:

  (swapargs '(module 7 5)) => (modulo 5 7)
And again:

  (swapargs (swapargs '(module 7 5))) => (module 7 5)
  (eval (swapargs '(module 7 5))) => 5
  (eval (swapargs (swapargs '(module 7 5)))) => 2
  (eval (swapargs (swapargs '(module 10 8)))) => 2
It looks like confusion between literal and evaluable lists prompted the wrong conclusion, but in this case it's simple to rectify.

Of course, macros in Scheme/Lisp can easily become convoluted and bug-ridden as much as any code, even aside from arguments about the virtues of "hygienic" vs. "unhygienic" systems. Properly constructed, macros remain an essential feature of Lisp/Scheme languages.

BTW, if we're comparing qualities of programming languages, here's a real-life example showing the particular merit of Scheme. I took on the task of creating a complex application (a web server supporting multiple hosts) and decided to write it primarily in Scheme (and some C). The first version was up and running in less than half a year.

Inevitably, months after the project was deployed changes were necessary. Despite the length of time since last seen, the code wasn't obscure to me, it was easy to understand and pick up where I'd left off before. Definitely different from prior experiences.

The crux is getting a good grasp on its core, macrology perhaps among the harder parts. But understood, Scheme allows enhanced productivity, as I've known it more so than other languages "under load" in parallel situations.

1 comments

Thank you for the detailed reply.

When you say my example is incorrect, do you mean that it's incorrect in clojure, or only in scheme? I tried my examples in clojure and they appear to work and appear to demonstrate a lack of referential transparency. I assume that clojure is a valid lisp to make a point about metaprogramming and macros.

Also, it looks like it's fairly easy in scheme to show the same thing, which it looks like you started to do (I'm not sure whether you agree with me about that or not):

  (define-syntax swapargs
    (syntax-rules ()
      ((_ ls) (list (list-ref ls 0) (list-ref ls 2)
                    (list-ref ls 1)))))
  
  (define a 7)
  (define b 5)
  (eval (swapargs '(modulo a b))) => 5
  (define a 10)
  (define b 8)
  (eval (swapargs '(modulo a b))) => 8
The two calls to "eval" are identical, yet return different results. That breaks referential transparency.

"showing the particular merit of Scheme"

From what I know, I like lisps of various flavors. I just said that they didn't really speak to the kinds of problems that I deal with. Maybe if I wrote more lisp I would see why it does so, but currently I do not.

  > The two calls to "eval" are identical, yet return
  > different results. That breaks referential transparency.
You are right, I don't know much about the syntax of clojure, but the Scheme version works as I'd expect. Yes, the eval calls return different results, but then again, we'd expect to compute a different output for different inputs.

What I tried to show is that calling (swapargs (swapargs '(a b c))) will always return the original list, that is, demonstrates referential transparency. In the case of '(modulo a b), result of evaluation returns a the same result when repeatedly given the same a, b inputs.

The point of the macro was exchanging the second and third elements of the input list. Naturally for the modulo operation, the order of the inputs is significant, and exchanging the operands will give the "opposite" remainder as the result.

In your example the two calls to (eval ...) are not identical and the "different results" are perfectly correct, without implications for referential transparency.

Don't know what kind of applications you might have in mind, but of course, no PL is optimum in all domains. For the kinds of programs I've tackled, Lisp/Scheme has been a good fit. Or maybe it has to do with the way my brain works just as much as the purposes I am applying the language to. That wouldn't surprise me a bit.