Actually, the best arc code IMO uses a fifty-fifty mix of functional and sequential thinking....
> my "set x" functions were clobbering each other
As a beginner you probably want to use '= instead- That's the more general command. Most arc lispers will only use 'set in rare cases.
> (def ugh (string) ...
Avoid naming the variable 'string: It will probably be OK, but since arc has functions and variables in the same namespace (google "lisp-1") you're clobbering the 'string function.
Here's how I'd rewrite this code (as far as I understand it, which isn't very far, I'm afraid so this is a very very rough approximation :-)
(def ugh (str)
(let x 5
(forlen i str
(let v (trunc:/ 12 (+ i 1))
(if (is #\A str.i)
(++ x v)
(-- x v)))
x))
The short of it is, you wouldn't necessarily get rid of the set (the '++ and '-- are just variants of 'set)
If you really, really wanted to get rid of the "sets" you could write something as the following, which is in pure functional style (you might not want to do this, though... there's better languages for experimenting with a pure FP style, like Haskell)
(Again, very rough, untested code)
(def ugh (str)
(apply +
5
(map (fn (c i)
(let x (trunc:/ 12 n)
(if (is #\A c)
x
(- 0 x))))
str
(range 1 len.str))))
If writing in this "pure" style really interest you, I'd suggest switching from arc to Haskell first. After 6 months of Haskell, writing code without "sets" will become second nature. (At that point, you'll also realize what a pain in the ass Haskell programming is and will return to arc :-)
Oh, and the posting trick is to surround your code by empty lines and have it indented two spaces.
Another twist is to use the 'on interator which binds its first argument to each successive element and implicitly binds 'index to the index of that element.
arc> (on c "arc" (prn index ":" c))
0:a
1:r
2:c
nil
(def ugh (str)
(let x 5
(on c str
(let v (trunc (/ 12 (+ index 1)))
(if (is c #\A)
(++ x v)
(-- x v))))
x))
1. Regular quoting is far simpler and has a much lower cognitive load (i.e. there are less things you have to be careful about when you use it)
2. On occasion, you need to quote data that contains commas or comma-ats without expanding anything. For instance, you may want to quote a piece of code to send to eval and that could contain a macro.
Your points do carry some weight, though- However, quotes and backquotes are heavily used in the ugliest of ugly macrology, so having both symbols available makes life significantly easier in those cases IMHO even if the simple quote seems somewhat redundant.
You're right from the standpoint that I hadn't noticed the similarity and should have. Your solution is probably the best yet for the example I gave.
But it's different in that reduce only works on simple lists- My main motivation for this type of command is for working with arbitrary and complex data structures, not just lists.
I guess I should clarify this a bit. In other languages (e.g. OCaml), it's conventional for each data structure to provide an iterator and a fold/reduce function. In Arc, the convention seems to be to provide iterators but not folds. The above code shows that it's sufficient only to have iterators, and a left fold can be automatically derived.
Alternatively, we could have data structures provide a left fold and automatically derive an iterator:
Your suggested "primary design decisions" seem to concern exclusively syntax & reserved names for built-in operators. This is a very specific and very superfluous aspect, for the latter two are the thin outer skin of any programming language.
From what I have read, Arc goes much deeper in its design principles. Those can be enveloped as the thorough "bottom-upness" of design: both read & write access to the language, right through to its primitive elements.
And this is the direct result of Mr. Graham's popular relation of Arc's design philosophy (http://paulgraham.com/design.html): a programming language ought to strive to accomodate those who themselves are qualified to design languages, translators and compilers, among other things.
This is at the foundation of Lisp, and Arc is a promising language because its designers seem to have well understood that foundation.
What is left to add, though, are modern counterparts of those 1960-s innovations that made Lisp superior not only in principle, but in reality also.
Your 'best alternative for my example is extremely clever, but doesn't work:
> (= *list-of-numbers* (6 2 4 5))
> (best (fn (a b) (and odd.a (> a b))) *list-of-numbers*)
6
I knew something was wrong with it, but it took me like half an hour to realize the flaw :)
...it can of course be fixed with extra flagging (well, it'll still fail on only lists of even numbers by not returning nil) but then you'll lose the brevity advantage over my solution.
Something else that would be hard to develop, but possibly pretty innovative, is if an arc package system were tied to a git-like patching system:
This means that a package could "modify" instead of just "adding" to the code.
In most languages, this wouldn't be a feature that makes sense. In those languages, packages simply override code instead of mutating it. However, since a central tenet of arc is to have extreme minimalism, it would be interesting if packages could avoid adding unnecessary bulk to the base language by allowing mutation of existing code.
So, for instance, a package that improves a basic arc language feature could actually "clean out" the previous version of the code right out of the base language. (The packaging system would of course retain a copy of the old code, in case the package is ever removed and the previous code needs to be restored)
Or, a package could just be a patch on a core file (like ac.scm) that enhances the core in some way. This way, a package system could be used even for aggressive language experimentation.
Seems interesting, but I don't know if it could be implemented in a simple manner. I think it would create quite a lot of confusion especially in the following situation: package A requires package B, package C requires a "modified" B, and package D requires both A and C. The conflict doesn't seem solvable, even with namespaces: B and B modified would live in the same namespace, unless the latter modifies also the namespace.
I suggested adding "interfaces" to packages. So A might require <B>v1, while C requires <B>v1-modified. At the very least, loading C would fail if the copy of B available on the user's machine is the original that does not provide <B>v1-modified.
OF course, it still requires that the modified B provide the semantics for the original B so that it provides <B>v1 seamlessly.
Interesting idea, but I'm having a hard time visualizing how it would work. How do you patch source code after it is compiled and being used in a running image? I am aware of the existing (let old foo (def foo (...) (bar (old ...)))) pattern, but you seem to be implying something more that this... Or am I completely off on that aspect of it?