With regard to (2), to destructively append the list '(1 2 3) to xs, you can:
(zap join xs '(1 2 3))
"zap join" is several characters longer than ++, but zap has general utility.
I use the string function to concatenate strings. It seems to work identically to +, as long as the first argument is a string. I do give heterogeneous arguments to string usually, and I like seeing that they will clearly be coerced into a string.
I have a couple of ideas.
1. It would be little problem for me if Arc provided + as is and provided a "plus" function that worked only on numbers and allowed me to simply go (= + plus) and proceed normally. Unfortunately, that would break all the functions in arc.arc and so forth that use + to concatenate lists (which includes, among other things, the setforms function, which is used in expand=). It would be really nice if one could "freeze" the variable references in those functions, so that changing the global value of + wouldn't change what "+" in the bodies of those functions referred to.
2. If you use concatenation so much, perhaps we could allocate an ssyntax character for concatenation. & is currently used for andf (experiment with ssexpand to see). We could boot out that usage, or perhaps have "&&" become andf. Good/bad idea? (Precedent: my memory tells me the TI-89 uses & for string concatenation.)
Given the way I defined a Bank-Account, you could implement this rather simply:
(def deposit (x . args)
(apply x 'deposit args))
Or, with akkartik's implementation, which I agree is nicer and better:
(def deposit (x . args)
(apply x!deposit args))
And you could make another "object" and give it a distinct 'deposit "method" and it would work fine.
So... it seems that one can easily implement these things in Arc. If you have some project in mind for which you want OO stuff, you can just do the things posted in this thread, and tell everyone how it goes if you like.
If you think OO should be put into the core of the language, well, there's a certain resistance to adding features just because someone suggests it. The core would become large, disorganized, and difficult to maintain if we did that. So, you would have to give a convincing argument as to why it's worth putting in there, and a demonstration of how useful it is in some real project of yours is probably the best argument you could give.
I've spent some time thinking about how to extend it for multiple-dispatch, and I didn't want to also think about setting the arg index to dispatch on.
In case you didn't know, the point of 'atpos is to implement atstrings, an optional string interpolation feature that lets "@qty @(plural qty \"taco\") @@ $@price" compile so that it evaluates to things like "2 tacos @ $11". This option isn't enabled by default, so any Arc program that uses atstrings, such as news.arc, has to (declare 'atstrings t) first.
IMO, an @ at the end of an atstring is a syntax error, since it isn't escaping anything. It might be convenient to give "...@" a meaning like (sym "..."), (err "..."), or even just "...@@", but that's adding functionality rather than fixing bugs.
What other goodies does your arc.vim plugin have? Is your editor at all integrated with the arc repl? Lack of a repl that I could easily send arc code to was the reason I switched to emacs after years of using vim. These days, using emacs with viper (vim emulation mode), I don't miss vim at all.
String concatenation is particularly convenient after the + change for arc3.tar: http://arclanguage.org/item?id=9937. That's probably what I use + for most of the time.
I find thinking about whether or not I have to escape the "@" character is distracting
I find this is easier with proper syntax highlighting. My arc.vim ftplugin can detect if you have (declare 'atstrings t) and, if so, highlights the escaped parts of strings. That way, you know if @ is escaped just by glancing. But I don't mean to shamelessly plug, haha. I don't use atstrings either, but my reason is far lazier: in the middle of writing code, it's less effort to just use + than it is to declare then go back and start using @s.
I make heavy use of + for concatenation throughout my code. I prefer it for a few reasons:
1. I find myself concatenating lists frequently and I prefer that frequently used functions be short. join has 4 chars to +'s 1
2. I also find ++ convenient for modifying a list variable. What would you use for the equivalent for join? In Racket's style, it would be join!, but I don't see a good analogue in arc for your proposal.
3. I'm constantly doing string concatenation. + is good for that because it's a short function name and also because I expect it to work because it works like that in many popular high-level languages (python, ruby, javascript). I also don't like to use arc's "@" for string-escaping because I find thinking about whether or not I have to escape the "@" character is distracting.
For what it's worth, I practically never add numbers. I actually use subtraction much more than addition! So I don't mind whatever these operations are named--and I mind the speed even less, actually--but I can testify that this change would sorta hinder my own code at this point. :-p
You've almost certainly encountered this comment above the definition of 'join, but I'll paste it here just in case it elucidates anything:
What you describe is doable with closures, at least as far as your example goes. Your example can be implemented almost word-for-word in Arc as it stands:
obj is a macro that creates a hash table (you don't need to quote the keys, by the way). So, (Socket url port) will return a function that accepts 1 or more arguments, assumes the first is a method name, looks it up in the hash-table o, finds the corresponding function, and applies it to the rest of the arguments.
If you want to give your object thing internal state that you'll want to modify, use a let.
Here is an example that probably demonstrates most of what you're looking for. It is pretty much cribbed from SICP[0].
Am i wrong if I think that, in the end, the main difference between 'def'+'eval' and 'mac' is that 'mac' allows its code to be evaluated and expanded once, whereas 'def' needs to expand its code every time it is called ?
It's 'eval that expands its code once every time it's called. If you don't use 'eval at all, then all your arc code will be expanded exactly once, at the time you enter it at the REPL or load it from a file (since those are the points where 'eval is called internally).
When you enter the expression (def foo (x) (obj key x)) is at the REPL, it's evaluated as a two-step process: First it's compiled (which expands the (def ...) and (obj ...) macro forms), and then the compiled code is run. When it's run, the body of the function isn't run yet; instead, it's packed up into a procedure and stored as foo. When you call foo later, no expansion takes place, only running of already compiled code.
A better (but not perfect) way to use 'eval equivalently to 'mac is something like this:
(def mywhen (test . body)
`(if ,test (do ,@ body))
(eval `(time:for i 1 1000 ,(mywhen '(is i 666) '(prn "Oh no!"))))
If we wanted to make 'do a non-macro, we could do that too:
(def mydo body
`((fn () ,@body)))
(def mywhen (test . body)
`(if ,test ,(apply mydo body)))
(eval `(time:for i 1 1000 ,(mywhen '(is i 666) '(prn "Oh no!"))))
Finally, if we wanted to avoid the use of macros altogether, the example would turn into something like this:
; The procedure corresponding to an Arc macro can already be obtained
; using 'rep.
(assign mywhen rep.when)
(assign mytime rep.time)
(assign myfor rep.for)
(eval:mytime:myfor 'i '1 '1000 (mywhen '(is i 666) '(prn "Oh no!")))
I could make a few guesses as to why this kind of programming isn't popular in lisps, but my personal reason is that I tend to consider all operators to provide their own syntax, with procedure call syntax just being the default choice. For me, it's inconsistent to have the '(prn ...) syntax be quoted when the (mywhen ...) syntax isn't.
You can get around the problem also in the following "weird" way:
(let x ''(+ 1 2) (xrepeat 3 `(pr ,x)))
but the way you proposed is more "natural" since it respects the "rule" of quoting functions and variables passed at calling time.
Am i wrong if I think that, in the end, the main difference between 'def'+'eval' and 'mac' is that 'mac' allows its code to be evaluated and expanded once, whereas 'def' needs to expand its code every time it is called ?
Thank you for the answer, very interesting. Here's some more things I found:
1) actually the "reference to undefined identifier" error can be avoided un-quoting the variables at calling time, as in:
(let i 2 (eval `(+ ,i 1)))
(time:for i 1 1000 (mywhen `(is ,i 666) '(prn "Oh no!")))
2) the last expression is computed in 176 msec, whereas the regular macro 'when' is computed in 2 msec (much faster as you forecasted).
It deals with lists and strings well, which is decent: Arc's only other sequence-like type is the table (I don't think you'd ever want to treat symbols as a sequence of 1-character symbols; you'd just use a string). Tables would work better if they were properly coerced, cf. the comment above tablist and listtab in arc.arc.
The more I think about it, the more I like this model. Conceptually, it seems that map should behave like
(def map (f seq)
(map-as (type seq) f seq))
even if it's not implemented like that -- all the coercions would surely be slow. (Tangential: map would also need to handle multiple sequences.) But it makes more sense for map and coerce to at least have compatible behavior. Plus, map's current behavior is a degenerate case of the coerce-compatible map:
Btw, you need an additional quote to get values like your "x" into the eval in all cases... "blub " works because a literal string evaluates to itself, but consider a list like (+ 1 2)
Also, you got around the limitation that eval doesn't have access to the lexical variables of its caller by passing in the value of "x", but if you wanted to have your eval/macro to set the value of a variable, that would be harder.
As waterhouse mentioned, two differences between macros and eval are that macros are expanded once when your program is loaded, but eval has to expand and compile its argument every time its called. And like any function eval doesn't have access to the lexical variables of its caller unless you pass in values yourself.
Aside from that, macros and eval are quite similar in the sense that they both treat code as data, and so you have the opportunity to manipulate the code before it gets evaluated.
Due to the way eval works, it doesn't touch lexical variables. So:
arc> (let i 2 (+ i 1))
3
arc> (let i 2 (eval '(+ i 1)))
Error: "reference to undefined identifier: _i"
This isn't specific to Arc, by the way. In Racket and in Common Lisp, it doesn't work:
> (let ((i 2)) ;Racket
(eval 'i))
. . reference to undefined identifier: i
Thus, your version of when will not work the same way as the macro version when you try to do this, for example:
arc> (time:for i 1 1000 (mywhen '(is i 666) '(prn "Oh no!")))
Error: "reference to undefined identifier: _i"
Other than that little kink with lexical variables, though, using eval does do pretty much the same thing as using a macro.
One more thing: eval is also much less efficient if you intend to use it repeatedly. By definition, eval performs full syntax analysis every time you use it. In the below example, its argument is a constant, so a smart compiler could theoretically optimize it to run like normal code, but in general its argument would not be constant.
So, I recommend against using eval if you can avoid it. One use case is where you wait for the user to type in an expression, and you evaluate it and print the result.
You don't just ask, you ask using words like "babble" and "shred of evidence". Is that necessary? It sounds defensive of PLT. But perhaps you think my argument is poor. So let me try to clarify.
You seem to be saying reputations are irrelevant. They're a shortcut. They're for when the program I'd have to write to test some property would require a week to months of work. If you want to ignore reputations, be my guest. But reputation is how you found out about PLT in the first place. You didn't run some idealized experiment comparing every single platform in existence for precisely the nuanced characteristics of the program you were planning to write.
I hear lots of people online touting Erlang as high performance at hundreds of cores, and highly fault tolerant. Have you not heard this? Do I need to cite chapter and verse for things I assume people are aware of? I don't hear anybody saying similar things for PLT. Am I just not listening in the right places? Feel free to cite evidence yourself.
I use PLT and arc because of programming convenience. I don't spend much time thinking about how good the runtime quality is because it's frankly not that important to me. It doesn't give me much confidence when I see 12 segfaults a day on my readwarp server[1]. So yes, it does 'crash a lot'[2]. In this very thread you're switching away from using immutable cons pairs because they seem to be not working as advertised. Is that not 'evidence'?
You've said things like "if there's anything better than PLT.." so it sounds like you're thinking in terms of an absolute ordering that you can stack language/platform combinations on. Surely the ordering is in the context of your use case. Or would you write the linux kernel in PLT scheme? Based on what you know now? How would you scale news.arc to two servers? I don't care that PLT 'has message passing'; if I wrote something that required lots of machines I know I would switch to Erlang or C, and put up with crappy syntax. You can't have the best programming environment and the best runtime - because no single community focuses equally on both.
I was careless in my phrasing of 'some people'. Sorry. I didn't expect to get pounced on, and I didn't mean for the conversation to get yet more noisy. It takes two to do that.
Predicting the performance of an optimization is actually quite difficult. I myself had noticed in passing the namespace test being done at runtime, but it had never occurred to me that it would make such a dramatic difference.
You've made quite a valuable contribution by measuring the performance improvement.