Arc Forumnew | comments | leaders | submit | zck's commentslogin
1 point by zck 5380 days ago | link | parent | on: Noob question

The command you'll use will be different on Windows (new-alias) vs. Linux (alias) -- which are you using? I can dig up my Linux command once I get home.

I don't run Arc to take scripts, so I can't help you there, but I can get you so that arc drops you in a REPL.

-----

2 points by zck 5411 days ago | link | parent | on: Buckets vs. Alists?

Their names being better isn't really a good reason -- you could make bind and lookup work on alists too.

They have a good argument about the state being able to be shared -- that does make things quicker. I'm not sure how much, but being as that it's Steele and Sussman, I'm sure they did their homework.

-----

2 points by zck 5425 days ago | link | parent | on: Noob question.

As hasenj said, 'some is the right choice here. A slightly-worse choice is 'apply, as in (apply in (uvar user friends)).

I say it only so you'll know about it when you need it; when beginning Lisp myself, I kept trying to figure out how to "unbox" lists to apply the values within as separate arguments to a function.

-----

3 points by hasenj 5425 days ago | link

I don't think apply would work on macros:

  arc> (apply in (list 1 1 2 3))
  Error: "Can't coerce  #(tagged mac #<procedure: in>) fn"
You can eval a `() expression with ,@ inside:

  (eval `(in 1 ,@your_list))

  arc> (let a '(1 2 3 4) (eval `(in 1 ,@a)))
  t

-----

1 point by zck 5425 days ago | link

Ah, you're right. I always get tripped up by this.

-----

2 points by shader 5423 days ago | link

Me too. This is one of those cases where I keep wishing we had first class macros.

-----

1 point by evanrmurphy 5423 days ago | link

I heard pg & rtm tried first-class macros but the performance was unacceptable. I would really like to have tried them out, seen results from some performance tests... or something!

How difficult of a hack would it be to give Arc first-class macros again?

-----

3 points by aw 5422 days ago | link

If you want to try out first-class macros to see what they could do for you, that's easy enough: write an interpreter for Arc. It'd be slow of course, but enough so that you could try out some different kinds of expressions and see if you liked what you do with it.

-----

1 point by evanrmurphy 5422 days ago | link

Yes. I'm actually taking a similar approach with more instant gratification: try using a lisp that already has them. http://picolisp.com/5000/-2.html

-----

2 points by rocketnia 5422 days ago | link

I was a fan of fexprs not too long ago, and I still kinda am, but they lost their luster for me at about this point: http://arclanguage.org/item?id=11684

Quoting from myself,

Quote syntax (as well as fexprs in general) lets you take code you've written use it as data, but it does little to assure you that any of that computation will happen at compile time. If you want some intensive calculation to happen at compile time, you have to do it in a way you know the compiler (as well as any compiler-like functionality you've defined) will be nice enough to constant-propagate and inline for you.

I've realized "compiler-like functionality you've defined" is much easier to create in a compiled language where the code-walking framework already exists than in an interpreted language where you have to make your own.

If part of a language's goal is to be great at syntax, it has a conflict of interest when it comes to fexprs. They're extremely elegant, but user libraries can't get very far beyond them (at least, without making isolated sublanguages). On the other hand, the dilemma can be resolved by seeing that an fexpr call can compile into a call to the fexpr interpreter. The compiler at the core may be less elegant, but the language code can have the best of both worlds.

This is an approach I hope will work for Penknife. In a way, Penknife's a compiled language in order to support fexpr libraries. I don't actually expect to support fexprs in the core, but I may write a library. Kernel-style fexprs really are elegant. ^_^

Speaking of such Kernel-like libraries, I've thrown together a sketch of a Kernel-like interpreter written in Arc. It's totally untested, but if the stars have aligned, it may only have a few crippling typos and omissions. :-p https://gist.github.com/778492

Can't say I'm a fan of PicoLisp yet, though. No local variables at all? Come on! ^_^

-----

1 point by evanrmurphy 5422 days ago | link

> Can't say I'm a fan of PicoLisp yet, though. No local variables at all? Come on! ^_^

Hmm... not sure what you mean by this. I can define a local variable at the PicoLisp REPL using let just as I would in Arc:

  : x   
  -> NIL
  : (let x 5
      x)  
  -> 5
  : x     
  -> NIL
The rest of your comment was really interesting. Thanks for the links!

-----

1 point by rocketnia 5422 days ago | link

Sorry, I spoke too soon. I could tell 'let and function arguments would be possible, but I was a bit put off by http://software-lab.de/doc/ref.html#conv. From http://www.prodevtips.com/2008/08/13/explicit-scope-resoluti..., it sounds like dynamic scope. Is that right? (I'm not in a position to try it out at the moment. >.> )

Speaking of speaking too soon, I may have said "user libraries can't get very far beyond [an fexpr language's core syntax]," but I want to add the disclaimer that there's no way I actually know that.

In fact, I was noticing that Penknife's parse/compile phase is a lot like fexpr evaluation. The operator's behavior is called with the form body, and that operator takes care of parsing the rest, just like an fexpr takes care of evaluating the rest. So I think a natural fexpr take on compiler techniques is just to eval code in an environment full of fexprs that calculate compiled expressions or static types. That approach sounds really familiar to me, so it probably isn't my idea. :-p

-----

1 point by evanrmurphy 5422 days ago | link

No harm done. :) PicoLisp appears to have lexical scoping but dynamic binding, although my PLT is too weak to understand all the implications of that. From the FAQ:

> This is a form of lexical scoping - though we still have dynamic binding - of symbols, similar to the static keyword in C. [1]

> "But with dynamic binding I cannot implement closures!" This is not true. Closures are a matter of scope, not of binding. [2]

---

[1] http://software-lab.de/doc/faq.html#problems

[2] http://software-lab.de/doc/faq.html#closures

-----

2 points by rocketnia 5422 days ago | link

Sounds like transient symbols are essentially in a file-local namespace, which makes them lexically scoped (the lexical context being the file!), and that transient symbols are bound in the dynamic environment just like internal symbols are. So whenever lexical scope is needed, another file is used. Meanwhile, (====) can simulate a file break, making it a little less troublesome.

-----

1 point by evanrmurphy 5422 days ago | link

But the let example I gave a few comments ago didn't use a transient symbol. Why does it work?

I chatted with PicoLisp's author, Alexander Burger, yesterday on IRC. If I catch him again, I can ask for clarification about the scoping/binding quirks.

-----

2 points by rocketnia 5422 days ago | link

I think it works because while you're inside the let, you don't call anything that depends on a global function named x. :) That's in the FAQ too:

-

What happens when I locally bind a symbol which has a function definition?

That's not a good idea. The next time that function gets executed within the dynamic context the system may crash. Therefore we have a convention to use an upper case first letter for locally bound symbols:

  (de findCar (Car List)
     (when (member Car (cdr List))
        (list Car (car List)) ) )
;-)

http://software-lab.de/doc/faq.html#bind

-----

1 point by markkat 5425 days ago | link

'Unboxing' was one of the paths I was going down during my trial and error, actually. Just didn't feel right. Thanks for the heads-ups.

-----

1 point by zck 5437 days ago | link | parent | on: Multi-arg anonymous functions broken?

I don't use anarki, but I seem to recall the arguments to an anonymous function aren't _.0 and _.1 . Note that x.y expands to (x y).

I think you want _0 and _1, but I'm not sure.

To be clear, you want the function to be equivalent to (fn (x y) (= x y)) ? That's odd, since = is the setf operator, not equal.

-----

1 point by zck 5508 days ago | link | parent | on: A better syntax for optional args

Some optional arguments are better done as a list. The greater the number of optional arguments, though, the better off you are using keyword arguments.

-----

1 point by zck 5526 days ago | link | parent | on: Sorting int in arc programme

aw is right. You can also look at http://files.arcfn.com/doc/list.html for more about sorting.

-----

1 point by zck 5555 days ago | link | parent | on: Html test

No html, but here's the doc on formatting: http://arclanguage.org/formatdoc

-----

1 point by zck 5582 days ago | link | parent | on: Macro question

I'm sure you know this, but using in this way is reminiscent of Common Lisp's loop : http://cl-cookbook.sourceforge.net/loop.html

-----

3 points by zck 5586 days ago | link | parent | on: Forum for basic questions?

I'm not an arc expert, but I think that posting here would be just fine -- the community isn't large enough to warrant fracturing into multiple different forums. Besides, who better to answer questions than people hacking in Arc? They don't bite. :)

-----

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

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 5611 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 5611 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 5608 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 5608 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 5607 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 5606 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.

-----

More