Arc Forumnew | comments | leaders | submit | icemaze's commentslogin

Shameless plug: I discussed something similar here http://arclanguage.org/item?id=350

-----

3 points by drcode 5762 days ago | link

I like yours, too...

Yours is far less invasive into the language. The trade-off is it doesn't give you cheap local variable creation, which was my primary motivation.

-----

1 point by icemaze 5762 days ago | link

True. What about ehird's variation (from the same thread)? It's a little more complex but should accomplish what you're looking for, since all the work is done at read time. Didn't try it tho, so give it a spin and let us know ^_^

-----

2 points by drcode 5762 days ago | link

Well, pretty much any solution involving a macro isn't going to do what I want. By necessity, calling a macro requires parentheses and a token. My solution just changes two parentheses to curly braces and requires no extra tokens. It will always be more lightweight than any macro solution. That's the main goal of my feature.

-----

3 points by tokipin 5762 days ago | link

note aand works similarly, with the exception that it shorts on nil:

http://arcfn.com/doc/anaphoric.html#aand

-----

1 point by icemaze 5762 days ago | link

Wow, that's true! I don't use aand much so I didn't notice. Shorting on nil can definitely be a problem because sometimes you want to do something different; it's not very readable since "aand" suggests a boolean result; finally, my macro could be modified in different ways that diverge from the usual aand semantics (like having a special form to break from the pipeline).

Thanks for the comment.

-----

1 point by almkglor 5762 days ago | link

Certainly interesting.

-----

1 point by icemaze 5916 days ago | link | parent | on: aif bug and fix

I believe it should be:

  (<= (len body) 1)
Also, this doesn't work with odd arguments. Working to fix that.

-----

1 point by icemaze 5916 days ago | link

Here it is:

  (mac aif args
    (let ln (len args)
      (if (is ln 0) nil
          (is ln 1) (car args)
          `(let it ,(car args)
              (if it ,(cadr args) (aif ,@(cddr args)))))))

-----

1 point by icemaze 5916 days ago | link

Er, ok, byronsalty's version works fine. Stupid me. :P

-----

1 point by icemaze 5922 days ago | link | parent | on: Ask PG: Why the Perl Artistic License?

It is: "Permission to use it is granted under the Perl Foundations's Artistic License 2.0."

-----

2 points by icemaze 5922 days ago | link | parent | on: Technical Comments on Arc

"Lisp-1. IMO, lisp isn't best suited to a functional style. This is one of the many lies that Schemers (and apparently Arcers) tell themselves."

Why do you think so? Could you provide us with a couple of examples? Not having to use #' is nice.

-----

6 points by icemaze 5922 days ago | link | parent | on: This is probably a really bad idea.

This is really nice! ^_^

Truly innovative, pure hacker style. But it is also true that anonymity can make people a little more... daring. I think a registration form could be a good step towards sanity: a login makes people think twice, which is usually a good thing. Just a suggestion, see if it suits you.

-----

2 points by nex3 5921 days ago | link

I'm not sure this is necessary, at least yet. People seem to be quite well-behaved so far - the only breakage is accidental (and that's mostly me anyway :-/).

-----

1 point by icemaze 5923 days ago | link | parent | on: Weak reduce

True, but sometimes you want some particular value for those special cases. In case of a sum (reduce add ()) should return 0, which is the (mathematically) "right" result.

Is there a way to know the arity of a function?

-----

1 point by parenthesis 5923 days ago | link

Yes, I suppose the real solution is an optional initial value argument (as in CL reduce)), thus you can provide an appropriate identity value (e.g. 0 in the case of add).

Edit: as simonb has simultaneously given.

-----

2 points by icemaze 5923 days ago | link | parent | on: [RFC] Macro Fix

I'd like to point out that this problem happens even when you don't redefine primitives or core functions. I read somewhere (maybe even one of PG's books, can't remember) that this is one of the reasons why Common Lisp is a LISP-2: because of unhygienic macros.

This is especially relevant when you use many libraries: it's not possible to remember all of their exported symbols and sooner or later you are going to shadow one of them. It only takes one macro call, then, to trigger a possibly very tricky bug.

This is made even worse by Arc's special syntax: if a variable contains a string or a hash instead of a unary function, this won't be detected until much later. So this problem seem to be more relevant to larger programs.

Is there a more elegant workaround? Does the devteam has something in mind?

-----

2 points by jsg 5920 days ago | link

I agree. macro transformers would need to refer to the lexical context in which they were defined. strangely, a number of threads in this forum seem to indicate substantial resistance against implementing Arc macros any way other than as simple list manipulations. now granted that 1) straightforward list transformations are easy to understand, 2) there are limitations with _some_ macro systems that provide for lexical references to identifiers in the definition context, 3) inadvertently shadowing identifiers appearing in macroexpansions is unlikely in _other_ Lisps thanks to multiple namespaces, package systems and restrictions on shadowing certain identifiers, and 4) it is a lot more difficult to correctly implement a macro system that solves this problem. however, let's all agree that a hypothetical macro system that _could_ solve this problem would be vastly preferable. arguing against this on the grounds that accidentally shadowing identifiers is a problem you're willing to deal with manually (in a single-namespace Lisp with no module system and no shadowing restrictions) is analogous to arguing in favor of dynamic scoping over lexical scoping as a sensible default.

-----

1 point by icemaze 5923 days ago | link | parent | on: Show us your Arc code

Everybody seems to be in love with the step parameter. Would it be such an improvement over (for instance):

  (reverse (range 10)), or
  (map [* 2 _] (range 5)) ?
Do python guys use it that often?

-----

1 point by Xichekolas 5923 days ago | link

Yeah I'd much rather compose functions than have extra parameters to remember... but I'm not a Python guy either.

-----

1 point by apgwoz 5923 days ago | link

It's pretty inefficient to use reverse, if instead you can add an extra parameter, though I guess it's often negligible.

-----

2 points by icemaze 5923 days ago | link | parent | on: (un)hygienic macros

Boilerplate code? You mean all the commas? Please compare:

  (define-syntax unless
    (syntax-rules ()
      ((unless condition body ...)
       (if (not condition) (begin body ...)))))
with (Common Lisp):

  (defmacro unless (condition &body body)
    `(when (not ,condition)
       ,@body))
Unhygienic is shorter byte-wise and token-wise. But they are not so different, are they? If you need to prevent variable capture for some symbols, you need one more line with with-gensyms and the symbols to protect. Easy enough.

Otherwise, please provide some real code to talk about.

-----

3 points by simonb 5923 days ago | link

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 5923 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 5923 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 5923 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 5922 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 5922 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 5921 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 5923 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.

-----

3 points by icemaze 5923 days ago | link | parent | on: If I were in charge of Arc...

1. Seems unimportant to me. Probably it's mostly an aesthetical matter, thus very subjective.

2. I like the default. Macros are hard and the last thing I wanna do is learn TWO macro systems. Also, I really can't get scheme-style macros. They just won't click.

"Turning off" variable capture is pretty straightforward once you get it. And there are tools that can help you do it easily. It's not the most convenient thing in the world, I admit, but it's good enough for me.

3. Yes, I like uniformity too. Why not `(a b . ,others) instead? Would be more similar to what we do elsewhere. Oh, well...

-----

1 point by bogomipz 5921 days ago | link

You can already do `(a b . ,others) in any Lisp. I just checked PLT, Arc and SBCL.

-----

1 point by maxwell 5923 days ago | link

If it's only "good enough," it's probably not good enough...

-----

More