Arc Forumnew | comments | leaders | submitlogin
Anaphoric Local Scope
3 points by ylando 5251 days ago | 3 comments
Lisp programs contain nested function call; For example:

  (let results '((ex1 (1 0 1)) (ex2 (1 1 1)) (ex3 (0.5 0.7 1)))
     (/ (reduce + (alref results 'ex1)) 3))
There are three problems with this style: The first problem is that it is a one liner and some times they hide nasty bugs, The second is that it is in a reverse order, The last problem is that it is hard to debug.

So here is my a code that create an Anaphoric Local Scope:

  (def split@@ (lst (o acc))
    (if (empty lst) (list (rev acc))
      (caris lst '@) (cons (rev acc) (split@@ (cdr lst)))
      (caris lst '@@) (cons (cons 'binding-label (rev acc)) 
                              (split@@ (cdr lst)))
      (split@@ (cdr lst) (cons (car lst) acc))))

   (def _ascope (lst)
     (if (empty lst) ()
       (with (e (car lst) r (cdr lst))
         (if (caris e 'binding-label) 
                (list (join (cdr e) (_ascope r)))
             (caris e 'dbg)
                (cons (cdr e) (_ascope r))
             (if (empty r) (list e)
                  (cons `(= it ,e) (_ascope r)))))))

   (mac ascope lst `(let it nil ,@(_ascope (split@@ lst))))
And here is the example using the anaphoric scope:

  (ascope 
    let results '((ex1 (1 0 1)) (ex2 (1 1 1)) (ex3 (0.5 0.7 1))) @@
    alref results 'ex1 @
    dbg prn it @
    reduce + it @
    dbg prn it @
    / it 3 @
    dbg prn it)
It is longer but I think that it is safer and more readable.

An example of how one liners can hide nasty bugs: http://arclanguage.org/item?id=11556

What do you think?



6 points by fallintothis 5250 days ago | link

The first problem is that it is a one liner and some times they hide nasty bugs

As a corollary to "sometimes code hides nasty bugs". ;)

One-liners aren't intrinsically bug-prone. I'd even argue that they're often less buggy, just because there's less code to get wrong. Akkartik's problem is actually an example: the issue was data structure choice, and the fixed code was still one line.

The second is that it is in a reverse order

Depends on who you ask. Nested function calls read fine to me, but people have built entire languages to avoid them (e.g., http://factorcode.org/).

The last problem is that it is hard to debug.

Not if you have something like erp: http://arclanguage.org/item?id=8726.

  arc> (let results '((ex1 (1 0 1)) (ex2 (1 1 1)) (ex3 (0.5 0.7 1)))
         (erp:/ (erp:reduce + (erp:alref results 'ex1)) 3))
  (alref results (quote ex1)): (1 0 1)
  (reduce + (erp:alref results (quote ex1))): 2
  (/ (erp:reduce + (erp:alref results (quote ex1))) 3): 2/3
  2/3
What do you think?

From what I can tell, your macro is essentially aand (note that prn will return the item it printed).

  (let results '((ex1 (1 0 1)) (ex2 (1 1 1)) (ex3 (0.5 0.7 1)))
    (aand (alref results 'ex1)
          (prn it)
          (reduce + it)
          (prn it)
          (/ it 3)
          (prn it)))
Are there any cases where it's much different?

In general, I think erp is more useful. You should give it a try!

-----

1 point by ylando 5250 days ago | link

Thank you for your answer. I think erp macro look great and is very useful.

I still think that the bug in Akkartik code is a result of too complicated one liner.

-----

6 points by akkartik 5250 days ago | link

"I still think that the bug in akkartik code is a result of too complicated one liner."

I'll make 2 objections to that:

a) That particular case was not a bug, but a performance issue.

b) The response to bugs isn't a more verbose formulation. Verbosity has its own costs to pay. Patterns that you could see in a single screen can no longer fit side by side, which can cause their own bugs.

If one-liners are to be avoided, you could just replace the call to reduce in your example with an explicit loop. But that's a bad idea, right?

Imagine a program where "x += 1" should really be "x += 2". Saying "x += 2 // always make sure we're adding 2 to x." doesn't seem like the appropriate response. The appropriate response is practices like automated tests and 5 whys (http://en.wikipedia.org/wiki/5_Whys, http://www.startuplessonslearned.com/2008/11/five-whys.html)

---

Perhaps you're finding right-to-left hard to read. Stick with it; you'll find that it becomes easier to read with practice. Many of us started out with similar limitations. It's like learning to ride a bicycle; I can't explain why it was hard before and isn't anymore, but vast numbers of people had the same experience and you will very probably have it too. As you read more code you'll be able to read dense one-liners more easily. There is indeed a bound on how dense a line should be, but this example is nowhere near it.

Further reading: http://www.paulgraham.com/power.html, http://www.paulgraham.com/head.html, http://www.paulgraham.com/popular.html section 3 "Brevity"

-----