Arc Forumnew | comments | leaders | submit | simonb's commentslogin
2 points by simonb 5714 days ago | link | parent | on: Little languages

Why not abstract the last bit away as well?

  (def-file-finder-plugin name (spec) ...)
As far as the approach itself is concerned I don't think it's evil. Just good use of declarative programming.

-----

4 points by simonb 5786 days ago | link | parent | on: Fold

The problem with your definition of fold is, it's not tail-recursive, which makes it less then ideal for implementing other general utility functions.

Also, to implement map you either need a right-fold or reverse the results.

-----

2 points by skenney26 5786 days ago | link

The fold defined above is a right fold. As for defining other general utilities, this version of fold can be used to define any function that uses a right fold. For example:

  (def len (xs)
    (fold (xy (+ y 1)) 0 xs))

  (def sumlength (xs)
    (fold (fn (n (x y)) (list (+ n x) (+ 1 y))) '(0 0) xs))
Graham Hutton has written an excellent paper on fold called "A tutorial on the universality and expressiveness of fold" which includes these and other definitions.

-----

3 points by simonb 5786 days ago | link

This doesn't change the fact that your definition of fold blows the stack for large inputs.

P.S. the "you need right-fold" bit was obviously a brain-fart on my part, sorry.

-----

2 points by simonb 6133 days ago | link | parent | on: Poll: Where are you from ?

Slovenia.

-----

1 point by simonb 6172 days ago | link | parent | on: Unquoting

http://citeseer.ist.psu.edu/bawden99quasiquotation.html

-----

1 point by simonb 6173 days ago | link | parent | on: The real problem with unhygienic macros

It probably has more to do with if being one of the primitives (or axioms if you will).

-----

2 points by simonb 6173 days ago | link | parent | on: Self referencing lambda

I don't think rfn will help here. rfn is useful for macros to prevent shadowing self and perhaps some obscure tricks involving mutually recursive nested lambdas.

However in your case you can get rid of let with a simple function call:

  (def foldl (op initial sequence)
    ((afn (result rest)
        (if rest
          (self (op result (car rest)) (cdr rest))
          result))
     initial sequence))

-----

1 point by lojic 6173 days ago | link

Of course - I do that in JavaScript all the time. Thx for the tip. Of course, I still want to be able to define a lambda that is bound to a name that also calls itself by the name instead of using self within and the name outside.

-----

2 points by simonb 6173 days ago | link

I think that's one of the cases where CL labels form would be more elegant.

But luckily this is Lisp so it shouldn't be too hard to make with a wee bit smarter so it would transform

  (with (foo (fn ...)))
to

  (with (foo (rfn foo ...)))

-----

4 points by simonb 6174 days ago | link | parent | on: Weak reduce

A somewhat messy attempt at mimicking CL's reduce:

  (let init-default (uniq)      
    (def reduce (f xs (o init init-default))
      ((afn (xs) 
         (if (cddr xs) (self (cons (f (car xs) (cadr xs)) (cddr xs)))
             (cdr xs) (apply f xs)
             xs (car xs)
             (f)))
       (if (is init init-default) xs (cons init xs)))))
For reference:

"In the normal case, the result of reduce is the combined result of function's being applied to successive pairs of elements of sequence. If the subsequence contains exactly one element and no initial-value is given, then that element is returned and function is not called. If the subsequence is empty and an initial-value is given, then the initial-value is returned and function is not called. If the subsequence is empty and no initial-value is given, then the function is called with zero arguments, and reduce returns whatever function does. This is the only case where the function is called with other than two arguments."

http://www.lispworks.com/documentation/HyperSpec/Body/f_redu...

-----

3 points by simonb 6174 days ago | link | parent | on: (un)hygienic macros

To make it perfectly clear, I am not advocating Scheme's implementation of hygienic macros. I am merely pointing out with-gensyms is such a common pattern it would make sense to abstracted it away. Furthermore we already have such an abstraction, it is called hygienic macros.

In a language where commonly used names are abbreviated to save a character or two, "one more line" of boiler-plate should not be accepted at face value.

Ideally hygienic macros would look exactly the same as unhygienic with some clever transformations behind the scenes. For instance instead of

  (mac complement (f)
    (let g (uniq)
      `(fn ,g (no (apply ,f ,g)))))
one would write:

  (hmac complement (f)
    `(fn g (no (apply ,f g)))))
and macro hmac would do the rest (probably arriving at something resembling the former).

-----

2 points by icemaze 6174 days ago | link

Oh ok. Now I understand your point better.

My points:

1. with-gensyms (or w/uniq in Arc) is not so common. I recently wrote a (small) application in Common Lisp and less than 50% of the macros need it. Same thing for once-only.

2. Yes, it would be a great thing to abstract that boilerplate away... if it was possible. I think Scheme developers aren't stupid. If it were easy to make a macro system that works like yours, it would probably already exist. As it's been argued, hygienic macros tend to make variable-capture much harder when you need it. And sometimes you need it a lot (e.g. DSL). Again: in my application, about 40% of macros capture a symbol, usually to make things shorter.

-----

2 points by elibarzilay 6174 days ago | link

Off topic, but:

* The define-syntax...syntax-rules boilerplate is a very-easy-to-bypass thing.

* Small applications are not a good justification for unhygienic macros: they bite much more when there are several people involved, or when a project lives for a long time.

* Adding automatic gensyms is not enough to call the result hygienic.

-----

1 point by icemaze 6173 days ago | link

1. I didn't know that, I never managed to get past that point. :P Are there macros like with-gensyms that make your life easier? Could you give a reference to show more precisely what you mean? That would be really nice.

2. What I mean is that variable capture is even more useful in big projects (because the DSL approach tends to be more powerful). It is true that they could cause nastier bugs if used incorrectly. But if you use them correctly (by forcing an order of evaluation and protecting the variables you use) there should be no problems. That's certainly true for a LISP-2, I'm not sure about Arc.

3. I don't care how you call it: if you use gensyms where needed the resulting macros won't screw you up! That's what matters, doesn't it?

-----

3 points by elibarzilay 6173 days ago | link

1. You complained about scheme having too much boilerplate code. This is easy to fix, for example, with this macro-defining-macros:

  (define-syntax defmac
    (syntax-rules ()
      ((defmac (name x ...) body)
       (define-syntax name
         (syntax-rules () ((name x ...) body))))))
you can write:

  (defmac (unless condition body ...)
    (when (not condition) body ...))
(See also item 4.)

2. Yes, DSLs are even more useful in big projects; obviously, the only source of all problems is using something incorrectly, the problem is how easy it is to write something incorrect; evaluation order has nothing to do with macros; protecting variables you use doesn't cover all problems; lisp-2 is "statistically better" because people shadow function names less frequently (see also next item).

3. They will bite -- even if you always use gensyms. A simple example:

  (def twice (func) [func (func _)])
  (mac with-duplicate (func . body)
    `(let ,func (twice ,func) ,@body))
This looks simple enough -- but `with-duplicates' happily inserts a `twice' symbol, regardless of what it happens to be bound to:

  (let twice [+ _ _] (with-duplicate cdr (cdr '(1 2 3 4))))
4. Extra point: doing simple captures in scheme with `define-syntax' is said to be difficult in the general case, but it is possible to make that easy too. For example, see the macro definition and examples in http://tmp.barzilay.org/defmac.ss -- the definition uses mzscheme's `syntax-case' etc, but this is not something that you should know about to use it. Just skip to the example to see how it works. (Cheat: there are subtle cases that this doesn't work.)

-----

1 point by icemaze 6173 days ago | link

1. Yes, that's much nicer, thanks.

2. "the problem is how easy it is to write something incorrect". Very true.

"evaluation order has nothing to do with macros" I'm sorry but that's incorrect. An example will show you why:

  (mac sum (x y) `(+ ,y ,x)) ; This is a toy, but some macros suffer from the same problem without being so trivial/stupid
  (= x 1)
  (+ x (++ x))   ; -> 3, the expected result
  (= x 1)
  (sum x (++ x)) ; -> 4, wrong result
Common Lisp macros usually solve this with a meta-macro called once-only that forces things to be evaluated in the right order and only once. Google it for more info.

"lisp-2 is statistically better". Well, not if people know that shadowing function can cause problems. Now a question arises: does shadowing functions solve problems in a way not possible with other methods? Does shadowing function make programs shorter? Or, equivalently, is shadowing functions really that useful? (Please provide examples if you answer this). If not, using a lisp-2 and being careful is enough. But yes, it's easy to overlook a symbol and make a mistake that can be costly. On that, I agree.

3. You are right and that's why I said "I'm not sure about [a lisp-1 like] Arc".

4. That seems really interesting, I'll look into it.

-----

1 point by elibarzilay 6172 days ago | link

You didn't understand me.

One of the things that you can do with macros is specify evaluation order, that's correct. You should also know when and how to evaluate your arguments, as done with the once-only utility (which is really just a thin wrapper around the obvious solution of binders).

But when I said "evaluation order has nothing to do with macros" I meant the kind of macro facility that you use (the original context of this thread was hygiene). No matter which kind of macros you have, the above issues are still the same.

-----

2 points by randallsquared 6174 days ago | link

I don't think it would resemble the former, if only because you'd want to use uniq calls for functions and macros in the expansion, lest they be redefined between here and the use site.

-----

1 point by simonb 6174 days ago | link | parent | on: Show us your Arc code

A tail recursive map1:

  (def map1 (f xs (o acc))
    (if (no xs) 
        acc
        (map1 f (cdr xs) (cons (f (car xs)) acc))))
Inspired by andf and orf (one predicate applied many operands):

  (def orl (f)
    (fn xs (some f xs)))

  (def andl (f)
    (fn xs (all f xs)))
It would be interesting to see which is the more common case and change built-in predicates to except multiple operands accordingly.

-----

2 points by simonb 6174 days ago | link | parent | on: syntax detail: (o bar 12) ==> (bar? 12)

Yet. I for one sincerely hope the lack of keyword parameters is only temporary.

-----

More