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

I have an idea for a programming language that can be use like the normal mode in vim or as a rpn calculator instead of dc.

-----

1 point by akkartik 4675 days ago | link

Can you elaborate? The readme didn't mention normal mode in vim.

-----

2 points by ylando 4674 days ago | link

Sorry I fix a namespace problem and now it works in gambit scheme and in r5rs language of racket. About normal mode in vim: I meant that it can be used as a real time scripting language. Suppose that you write your own program, It will be easy for the user to press ESC write dynamic forth expression and press enter. If he will do the same with arc, he will have to worry about parentheses. He will also have trouble using the result of the last calculation.

-----

1 point by akkartik 4674 days ago | link

Ah thanks. In racket I need to add this line to the top of the generated file:

  #lang r5rs
(There doesn't seem any commandline flag to specify the language.)

-----


Why arc do not have a normal web page; See:

  http://www.ruby-lang.org/en/
  http://www.python.org/
  http://www.perl.org/
  http://www.erlang.org/
  http://clojure.org/

-----

2 points by akkartik 5235 days ago | link

Because it's unfinished (and may remain so). See http://arclanguage.org and more recently http://news.ycombinator.com/item?id=1525323. No point sucking people in with a normal-looking webpage if the language isn't really ready for production use.

-----

1 point by evanrmurphy 5234 days ago | link

Could you talk about your decision to use it for Readwarp then? If Arc's not really ready for production use, might it still be a good choice for a certain minority of developers?

-----

2 points by akkartik 5234 days ago | link

Yeah, I'm not trying to say you shouldn't use it for production use :)

They're opposing perspectives. As a user of arc I'd throw it into production[1]. At the same time, from PG's perspective I'd want to be conservative about calling it production ready.

I suspect arc will never go out of 'alpha' no matter how mature it gets, just because PG and RTM will not enjoy having to provide support, or having to maintain compatibility.

[1] With some caveats: treat it as a white box, be prepared to hack on its innards, be prepared to dive into scheme and the FFI. And if you're saving state in flat files, be prepared for pain when going from 1 servers to 2.

Not all kinds of production are made the same.

-----


I have another idea:

  We can combine the next macro with the ":" shortcut
  (mac c ((obj (func . args)))
    '(call ,obj ,func ,@args)))
  
  (= document!body!innerHTML 
    ((c:document:getElementById "foo") value))
  It will translate to:
  (= ((document 'body) 'innerHTML)
     ((call document getElementById "foo") value))

-----

1 point by evanrmurphy 5245 days ago | link

Interesting, but where is call defined?

Also a nitpick: your c macro needs quasiquote and has too many parens:

  (mac c (obj (func . args))
    `(call ,obj ,func ,@args)))
I'm not quite sure how the args in that macro are supposed to work. Is it somehow taking advantage of destructuring bind, or why is it (obj (func . args)) instead of just (obj func . args)

Thanks for the suggestion. Looking forward to hearing you elaborate.

-----

1 point by ylando 5244 days ago | link

It supposes to be:

  (mac c (obj (func . args))
     `(call ,obj ,func ,@args))
Call is a macro for calling functions in object system. If you don't have one you can use something like this:

  (mac c (obj (func . args))
     `(,func ,obj ,@args))
It use destructoring bind of the compose functions:

  (c:obj!sub-obj:func arg1 arg2)
Translate into:

  (c ((obj 'sub-obj) (func arg1 arg2)))

-----

1 point by ylando 5247 days ago | link | parent | on: Class macros

I have a use case. If we have an object system and we want to implement the "each" macro for every collection.

-----

1 point by evanrmurphy 5247 days ago | link

  (w/speculation
Much of Lisp's power stems from the fact that virtually everything can be represented as a list. If this is true, then writing 'each for lists is almost as good as (or better than) writing a generic 'each for every kind of type. A language design like Arc's that capitalizes on this idea indirectly provides incentive for making as many things into lists as possible. This is why pg has flirted with representing both strings [1] and numbers [2] as lists, and also why he promotes using assoc lists over tables when possible [3].

One disadvantage of this approach is that it can sometimes seem unnatural to represent x as a list, but it has the benefit of providing a very minimal cloud of abstractions with maximum flexibility.

A powerful object system seems like a different way of going about the same thing. That's probably why Lisp users are often unenthusiastic about objects: there's a feeling of redundancy and of pulling their language in two different directions. (Arc users can be especially unenthusiastic because they're so anal about minimizing the abstraction cloud.) That's why I'm not particularly enthusiastic about objects, anyway. They're not worse - just different, and largely unnecessary if you have lists.

I could be missing the ship here. I don't have enough experience with object systems to understand all their potential benefits over using lists for everything. (And, of course, the popularity of CLOS demonstrates that a lot of people like to have both!)

  )
[1] arc.arc has this comment toward the top:

  ; compromises in this implementation: 
  ...
  ; separate string type
  ;  (= (cdr (cdr str)) "foo") couldn't work because no way to get str tail
  ;  not sure this is a mistake; strings may be subtly different from 
  ;  lists of chars
[2] See this quote from http://www.paulgraham.com/hundred.html and its subsequent paragraphs:

The Lisp that McCarthy described in 1960, for example, didn't have numbers. Logically, you don't need to have a separate notion of numbers, because you can represent them as lists: the integer n could be represented as a list of n elements. You can do math this way. It's just unbearably inefficient.

[3] See "Assoc-lists turn out to be useful." in http://www.paulgraham.com/arclessons.html and

  I once thought alists were just a hack, but there are many things you can
  do with them that you can't do with hash tables, including sort
  them, build them up incrementally in recursive functions, have
  several that share the same tail, and preserve old values.
from http://ycombinator.com/arc/tut.txt.

-----

4 points by rocketnia 5246 days ago | link

I think you're right about it being frustrating to be pulled in multiple directions when choosing how to represent a data structure.

In Groovy, I'm pulled in one direction:

  class Coord { int x, y }
  ...
  new Coord( x: 10, y: 20 )
    + okay instantiation syntax
    + brief and readable access syntax: foo.x
As the project evolves, I can change the class definition to allow for a better toString() appearance, custom equals() behavior, more convenient instantiation, immutability, etc.

In Arc, I'm pulled in about six directions, which are difficult to refactor into each other:

  '(coord 10 20)
    + brief instantiation syntax
    + brief write appearance: (coord 10 20)
    + allows (let (x y) cdr.foo ...)
    - no way for different types' x fields to be accessed using the same
        code without doing something like standardizing the field order
  
  (obj type 'coord x 10 y 20)
    + brief and readable access syntax: do.foo!x (map !x foos)
    + easy to supply defaults via 'copy or 'deftem/'inst
  
  [case _ type 'coord x 10 y 20]
    + immutability when you want it
    + brief and readable access syntax: do.foo!x (map !x foos)
    - mutability much more verbose to specify and to perform
  
  (annotate 'coord '(10 20))
    + easy to use alongside other Arc types in (case type.foo ...)
    + semantically clear write appearance: #(tagged coord (10 20))
    + allows (let (x y) rep.foo ...)
    - no way for different types' x fields to be accessed using the same
        code without doing something like standardizing the field order
  
  (annotate 'coord (obj x 10 y 20))
    + easy to use alongside other Arc types in (case type.foo ...)
    + okay access syntax: rep.foo!x (map !x:rep foos)
  
  (annotate 'coord [case _ x 10 y 20])
    + immutability when you want it
    + easy to use alongside other Arc types in (case type.foo ...)
    + okay access syntax: rep.foo!x (map !x:rep foos)
    - mutability much more verbose to specify and to perform
(This doesn't take into account the '(10 20) and (obj x 10 y 20) forms, which for many of my purposes have the clear disadvantage of carrying no type information. For what it's worth, Groovy allows forms like those, too--[ 10, 20 ] and [ x: 10, y: 20 ]--so there's no contrast here.)

As the project goes on, I can write more Arc functions to achieve a certain base level of convenience for instantiation and field access, but they won't have names quite as convenient as "x". I can also define completely new writers, equality predicates, and conditional syntaxes, but I can't trust that the new utilities will be convenient to use with other programmers' datatypes.

In practice, I don't need immutability, and for some unknown reason I can't stand to use 'annotate and 'rep, so there are only two directions I really take among these. Having two to choose from is a little frustrating, but that's not quite as frustrating as the fact that both options lack utilities.

Hmm, that gives me an idea. Maybe what I miss most of all is the ability to tag a new datatype so that an existing utility can understand it. Maybe all I want after all is a simple inheritance system like the one at http://arclanguage.org/item?id=11981 and enough utilities like 'each and 'iso that are aware of it....

-----

1 point by shader 5245 days ago | link

I rewrote the type system for arc a while ago, so that it would support inheritance and generally not get in the way, but unfortunately I haven't had the time to push it yet. If you're interested, I could try to get that up some time soon.

-----

1 point by rocketnia 5245 days ago | link

Well, I took a break from wondering what I wanted, and I did something about it instead, by cobbling together several snippets I'd already posted. So I'm going to push soon myself, and realistically I think I'll be more pleased with what I have than what you have. For instance, Mine is already well-integrated with my multival system, and it doesn't change any Arc internals, which would complicate Lathe's compatibility claims.

On the other hand, and at this moment it's really clear to me that implementing generic doppelgangers of arc.arc functions is a bummer when it comes to naming, and modifying the Arc internals to be more generic, like you've done (right?), could really make a difference. Maybe in places like that, your approach and my approach could form an especially potent combination.

-----

2 points by rocketnia 5232 days ago | link

I finally pushed this to Lathe. It's in the new arc/orc/ folder as two files, orc.orc and oiter.arc. The core is orc.arc, and oiter.arc is just a set of standard iteration utilities like 'oeach and 'opos which can be extended to support new datatypes.

The main feature of orc.arc is the 'ontype definition form, which makes it easy to define rules that dispatch on the type of the first argument. These rules are just like any other rules (as demonstrated in Lathe's arc/examples/multirule-demo.arc), but orc.arc also installs a preference rule that automatically prioritizes 'ontype rules based on an inheritance table.

It was easy to define 'ontype, so I think it should be easy enough to define variants of 'ontype that handle multiple dispatch or dispatching on things other than type (like value [0! = 1], dimension [max { 2 } = 2], or number of arguments [atan( 3, 4 ) = atan( 3/4 )]). If they all boil down to the same kinds of rules, it should also be possible to use multiple styles of dispatch for the same method, resolving any ambiguities with explicit preference rules. So even though 'ontype itself may be limited to single dispatch and dispatching on type, it's part of a system that isn't.

Still, I'm not particularly sure orc.arc is that helpful, 'cause I don't even know what I'd use it for. I think I'll only discover its shortcomings and its best applications once I try using it to help port some of my Groovy code to Arc.

http://github.com/rocketnia/lathe

-----

1 point by shader 5245 days ago | link

Well, I guess we'll just have to find out ;)

And yes, I did modify arc's internals to be more generic. Basically, I replaced the vectors pg used for typing with lists and added the ability to have multiple types in the list at once. Since 'type returns the whole list, and 'coerce looks for conversion based on each element in order, we get a simple form of inheritance and polymorphism, and objects can be typed without losing the option of being treated like their parents.

-----

3 points by rocketnia 5247 days ago | link

For that example, what about just defining a 'walk method for every collection and defining 'each the way Anarki does?

  (mac each (var expr . body)
    `(walk ,expr (fn (,var) ,@body)))

-----

1 point by ylando 5246 days ago | link

"Each" can iterate over millions of elements; in this case, we can use class macros to remove the function calls.

-----

2 points by fallintothis 5247 days ago | link

If we have an object system

Which explains why I've never needed object-oriented macros. :)

-----

1 point by ylando 5247 days ago | link | parent | on: Problems with arc

Anyone have any ideas on how to allow macro indirection via namespaces without having first class macros?

You can do something similar to my class macros, see:

  http://arclanguage.org/item?id=12003
You only need to declare namespace instead of class and it will work.

-----

1 point by ylando 5247 days ago | link | parent | on: Class macros

I have a solution to a small problem with the code. Put:

  (def macex-all (e)
    (zap macex e)
    (if acons.e
        (map1 macex-all e)
        e))

  (mac dec (obj type . body)
    (let oldtype (declare-obj-class* obj) 
       (= (declare-obj-class* obj) type)  
       (let exp-body (macex-all body)
            (= (declare-obj-class* obj) oldtype)
            (cons 'do exp-body))))
and remove undec and temporary*. This fixes the problem of declaration wasting run time.

-----

4 points by fallintothis 5247 days ago | link

That version of macex-all is a bit primitive.

  arc> (macex-all '(fn (do re mi) (+ do re mi)))
  (fn ((fn () re mi)) (+ do re mi))
  arc> (macex-all ''(do not expand this -- it is quoted))
  (quote ((fn () not expand this -- it is quoted)))
If you want a more robust macex-all, you can gank my implementation from http://arclanguage.org/item?id=11806 (rewritten slightly):

  (def imap (f xs)
    (when xs
      (if (acons xs)
          (cons (f (car xs)) (imap f (cdr xs)))
          (f xs))))

  (def macex-all (expr)
    (zap macex expr)
    (check expr
           atom
           (case (car expr)
             quasiquote (list 'quasiquote
                              ((afn (level x)
                                 (if (is level 0)
                                      (macex-all x)
                                     (caris x 'quasiquote)
                                      (list 'quasiquote (self (+ level 1) cadr.x))
                                     (in acons&car.x 'unquote 'unquote-splicing)
                                      (list car.x (self (- level 1) cadr.x))
                                      (check x atom (imap [self level _] x))))
                               1 (cadr expr)))
             fn         `(fn ,(cadr expr) ,@(imap macex-all (cddr expr)))
             quote      expr
                        (imap macex-all expr))))
This also won't break on dotted lists, like

  arc> (macex-all '`(a . b))
  Error: "Can't take car of b"
But if you don't care about that, you can replace all the uses of imap with map1 or map.

Also, a couple nitpicks about dec.

  (macex-all body)
breaks, because body is a list, so if its car is a macro, macex-all thinks it's a macro call.

  arc> (ppr:macex1 '(dec foo bar
                      mac baz quux quack))
  (do (fn ()
        (sref sig 'quux 'baz)
        ((fn ()
           (if (bound 'baz)
               ((fn ()
                  (disp "*** redefining " (stderr))
                  (disp 'baz (stderr))
                  (disp #\newline (stderr)))))
           (assign baz
                   (annotate 'mac (fn quux quack)))))))t
Notice too that consing do to the front breaks in this case. So, you should use

  (map1 macex-all body)
You won't need to care about dotted lists, since body will be proper (rest parameters in Arc always are).

Finally, the pattern

  (let result x
    ... stuff not involving x or result ...
    result)
is exactly what do1 is for. Altogether, that's

  (mac dec (obj type . body)
    (let oldtype (declare-obj-class* obj)
      (= (declare-obj-class* obj) type)
      (do1 (cons 'do (map1 macex-all body))
           (= (declare-obj-class* obj) oldtype))))

-----

1 point by ylando 5252 days ago | link | parent | on: Problems with arc

Are you saying that you might accidentally assign to a misspelled variable?

Yes this is exactly the problem. This the reason perl has: use strict.

-----

1 point by ylando 5252 days ago | link | parent | on: Suggestion: how to improve arc.

Suppose we want to declare a name inside a function We must use it outside of the function definition For example:

  (declare int_value int
  (def myfunc (...) ...
     (let int_value 3 ... )))

-----

2 points by fallintothis 5252 days ago | link

I still don't follow. A) Why do we need "ignores" here? B) Why would we need to use the declaration outside of the definition? Expressions can be sequenced. I.e., wouldn't you do something like this?

  (def f (x y)
    (let i 3
      (declare integer i)      ; this is evaluated and sets metadata...
      (something-with x y i))) ; ...but THIS is the value that gets returned
Similarly,

  (def foo (bar)
    (let baz 10
      (prn "hello") ; evaluates and returns the string "hello"
      (+ bar baz))) ; evaluates and returns bar + 10

  arc> (foo 5) ; prints hello and returns 15
  hello
  15

-----

1 point by ylando 5252 days ago | link

In the example above you waste run time on declaring i as integer.

Suppose we want to declare the value for a region of code; like a let statement. We can do it in the following way:

  (mac declare (name prop . body) 
     (let oldprop (metadata* name)
       (= (metadata* name) prop)
       `(do (= temporary* (do ,@body)) (undeclare ,name ,oldprop))))

  (mac undeclare (name oldprop) 
    (= (metadata* obj) oldprop)
    'temporary*)
If we put it in a function body, we still waste time.

-----

1 point by zck 5252 days ago | link

I think fallintothis declared i as an integer because ey was trying to translate your code, which declares int_value as an integer.

So the 'temporary call serves to tell the 'do in 'declare not to return the value from 'undeclare, but instead to return the value from the (= temporary* (do ,@body)) line?

This seems a lot more complicated than simply saving the value in a 'let or using do1.

-----

2 points by fallintothis 5252 days ago | link

This seems a lot more complicated than simply saving the value in a 'let or using do1.

There's also a typo:

  (mac undeclare (name oldprop)
    (= (metadata* name) oldprop)    ; was (= (metadata* obj) oldprop)
    'temporary*)
Even fixing that, the metadata-setting happens at macroexpansion time, so you get

  arc> (def f (x) (declare x integer (prn "metadata*: " metadata*) (+ x 5)))
  #<procedure: f>
  arc> metadata*
  #hash()
  arc> (f 5)
  metadata*: #hash()
  10
  arc> metadata*
  #hash()
At no point before, after, or in the body is the metadata actually in the hash table. It was just there for a brief pause between the macroexpansions of declare and undeclare.

In all, I would've written it

  (mac declare (name prop . body)
    `(after (do (= (metadata* ',name) ',prop)
                ,@body)
            (= (metadata* ',name) ,(metadata* name))))
But the last line shows we just wipe any declaration we made, so a global metadata table gets messy, unless we make the declarations themselves global (i.e., get rid of body).

-----

4 points by rocketnia 5252 days ago | link

It was just there for a brief pause between the macroexpansions of declare and undeclare.

If we want to change the behavior of other macros for a certain region of code, then that pattern might be useful. Since we seem to be talking about static type declarations, which I presume would be taken into account at macro-expansion time, I think the "between the macroexpansions" behavior is the whole point.

-----

1 point by fallintothis 5249 days ago | link

Thank you for the insight. It's probably the most lucid I've been all thread. It didn't seem deliberate to me, but it could have feasibly been written that way to control other macros' expansions. This also pushes computation to expansion time, which might clarify ylando's objections about "wasting run time". Except those still confuse me: macro expansion happens once, inside a function's body or outside of it.

  arc> (mac m (expr)
         (prn "macro m has expanded")
         expr)
  #(tagged mac #<procedure: m>)
  arc> (def f (x)
         (m (+ x 1)))
  macro m has expanded
  #<procedure: f>
  arc> (f 1)
  2
But the original point seems lost because declare's story keeps changing. So, ylando: why do we need "ignores"?

-----

1 point by ylando 5249 days ago | link

why do we need "ignores"?

Try building a macro that change global value, expand code (with macros) and then change the value back. I think that this macro must use another macro to change the value back; like the undeclare macro above. The second macro expands into unnecessary code; so if you put it inside a function this unnecessary code will waste run time. If we have "ignore" macro, we can write macros that do not produce unnecessary code.

-----

4 points by fallintothis 5248 days ago | link

I think that this macro must use another macro to change the value back; like the undeclare macro above.

Not necessarily.

  (mac declare (name prop . body)
    (let old (metadata* name)
      (= (metadata* name) prop)
      `(after (do ,@body)
         (= (metadata* ',name) ,old))))
But this introduces redundant assignments after each execution of body. Using a macro that expands into "unnecessary code" can be cheaper.

  (mac declare (name prop . body)
    (let old (metadata* name)
      (= (metadata* name) prop)
      `(after (do ,@body)
         (undeclare ,name ,old))))

  (mac undeclare (name old)
    (= (metadata* name) old)
    nil)
This introduces a redundant nil in the after block, and using after is a bit slower than just a do1. But we can't use do1 because this "do all the work at macro-expansion" approach is so touchy that it breaks:

  arc> (load "macdebug.arc") ; see http://arclanguage.org/item?id=11806
  nil
  arc> (macwalk '(declare name prop a b c))
  Expression --> (declare name prop a b c)
  macwalk> :s
  Macro Expansion ==>
    (do1 (do a b c)
         (undeclare name nil))
  macwalk> :s
  Macro Expansion ==>
    (let gs2418 (do a b c)
      (undeclare name nil)
      gs2418)
  macwalk> :s
  Macro Expansion ==>
    (with (gs2418 (do a b c))
      (undeclare name nil)
      gs2418)
  macwalk> :s
  Macro Expansion ==>
    ((fn (gs2418)
       (undeclare name nil)
       gs2418)
     (do a b c))
  macwalk> :s
  Subexpression -->
    (fn (gs2418)
      (undeclare name nil)
      gs2418)
  macwalk> :s
  Subexpression --> (undeclare name nil)
  macwalk> :s
  Value ==> nil
  Value ==> gs2418
  Value ==> (fn (gs2418) nil gs2418)
  Subexpression --> (do a b c)
  macwalk> :a
  Value ==> (do a b c)
  Value ==>
    ((fn (gs2418) nil gs2418) (do a b c))
  ((fn (gs2418) nil gs2418) (do a b c))
Note that we reach undeclare before the actual body is expanded!

We can hack it without after or do1 (or mutation, but I avoid that anyway).

  (mac declare (name prop . body)
    (let old (metadata* name)
      (= (metadata* name) prop)
      (w/uniq (g1 g2)
        `(with (,g1 (do ,@body)
                ,g2 (undeclare ,name ,old))
           ,g1))))

  (mac undeclare (name old)
    (= (metadata* name) old)
    nil)
This way, declare expands in the right order and we only undeclare once, since it'll expand into nil. The nil is "unnecessary", which seems to be why you want ignore, but it's a terribly pedantic point: ignore is already accomplished by dead code elimination (http://en.wikipedia.org/wiki/Dead_code_elimination). This isn't even a case of "sufficiently smart compilers" for vanilla Arc, since mzscheme already implements the standard optimizations: function inlining, dead code elimination, constant propagation/folding, etc. (see http://download.plt-scheme.org/doc/html/guide/performance.ht...) should all be able to clean up whatever ac.scm generates. E.g.,

  (mac foo ()
    `(prn ',metadata*!name))

  (declare name bar (foo))
compiles to

  ((lambda (gs581 gs582) gs581)
   ((let ((| gs581| (lambda () (ar-funcall1 _prn 'bar)))) | gs581|))
   'nil)
which should be easy to optimize.

Worrying seems premature anyway. ac.scm and arc.arc themselves do plenty worse, and the differences here are essentially rounding error.

  arc> (time10 (w/stdout (outstring)
                 (repeat 1000000
                   (with (x (do (prn 'bar))
                          y nil)
                     x))))
  time: 43048 msec.
  nil
  arc> (time10 (w/stdout (outstring)
                 (repeat 1000000
                   (prn 'bar))))
  time: 43170 msec.
  nil
Final idea: if expansion-time computation can't be avoided, you can expand the macros manually, if only for the sake of your readers. As a bonus, it does away with the dead code.

  (mac declare (name prop . body)
    (let old (metadata* name)
      (= (metadata* name) prop)
      (do1 `(do ,@(map macex-all body)) ; see http://arclanguage.org/item?id=11806
           (= (metadata* name) old))))
However, I think the fragile expansion-time computation is a bigger issue than dead code.

-----

1 point by ylando 5247 days ago | link

I try:

  (def macex-all (e)
    (zap macex e)
    (if acons.e
        (map1 macex-all e)
        e))

  (mac declare (name prop . body)
    (let old (metadata* name) 
       (= (metadata* name) prop)  
       (let exp-body (macex-all body)
            (= (metadata* name) old)
            (cons 'do exp-body))))
and it works. thanks.

-----

2 points by ylando 5252 days ago | link | parent | on: Suggestion: how to improve arc.

Suppose that you want to write a macro that define a function:

  (mac def-func (func-name func-body)
    (w/uniq first-arg
      (w/uniq rest-arg
         `(def ,func-name (,first-arg . ,rest-arg)
             ,@func-body))))
And it will not work. There is a problem with using dot in quasiquote.

-----

3 points by shader 5252 days ago | link

So your real question is why dots don't work in quasiquote, and what should be done to fix them. That's a question that we can relate to a lot better than what we originally thought was "I don't like dots"

-----

2 points by evanrmurphy 5252 days ago | link

Would someone mind being more explicit about how it doesn't work? After dotting the outer definition's rest parameter as well so that the first line reads,

  (mac def-func (func-name . func-body)
it seems to be working for me:

  arc> (def-func 1+1 (+ 1 1))
  #<procedure: 1+1>
  arc> (1+1 nil)
  2
If you're interested, here's a version of your macro with a few things cleaned up (some changes only subjectively better):

  (mac def-func (name . body)
    (w/uniq (first rest)
      `(def ,name (,first . ,rest)
         ,@body)))

-----

1 point by ylando 5252 days ago | link

It don't work with jarc16, it don't work with arc3. What is your version of arc?

-----

1 point by evanrmurphy 5252 days ago | link

arc3.1. So did you get an error message on trying

  (def-func 1+1 (+ 1 1))
or what? Did you dot the func-body rest parameter as well?

-----

1 point by ylando 5252 days ago | link

It works on arc3.1.

-----

1 point by ylando 5260 days ago | link | parent | on: Anaphoric Local Scope

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 5259 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"

-----

More