First use & instead of dot in functions
before rest.
for example (def func (a b . rest) ...) =>
(def func (a b & rest) ... )
It will make macro writing much easier.
Second add a function that is ignored by the compiler.
It can be use when we write macros for metadata manipulation.
For example when we want to declare a name
to have a property: we can write:
Not sure exactly what you're going for with the "function ignored by the compiler" Maybe a few more examples would be more helpful?
In general, when you have things that you want to improve about arc, I would recommend one of two things:
1) If your change is merely syntactical, or is trying to get arc to use a different style of programming (oop, etc.) try doing things the arc way for a while, and see if you can't learn to appreciate it the way it is. It's quite possible that after a while you'll learn to prefer the traditional lisp/arc method.
2) If you're writing on the forum about changes that you'd like to see made to arc, try implementing them and submitting them first. This will allow the community to experiment with your ideas and give better criticism, as well as give you a greater understanding of why things are the way they are, and how hard it might be to change them.
This is an "open source" community. That means you have just as much opportunity to implement your desired feature as everyone else, but a better understanding of it and a lot more interest in getting it done. If you don't feel like implementing your feature, odds are no one else will either.
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
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.
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.
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).
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.
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"?
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.
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).
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))
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.
How would using ampersand over dot make macro writing much easier? Is it just that you find the notation more intuitive, or is there something else?
Personally I prefer the dot because I think it looks less jarring and there's the connection with improper lists, as pg mentions in the tutorial (http://ycombinator.com/arc/tut.txt):
(These conventions are not as random as they seem. The parameter
list mirrors the form of the arguments, and a list terminated by
something other than nil is represented as e.g. (a b . c).)
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"
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,