There has been a lot of criticism and praise of Arc. If pg's done one thing well, it's creating hype (and thus a community) around Arc.
I would like to ask, preferably in a constructive manner, what would you change in Arc if you could?
Hopefully we can get a full list of ideas that are useful for either pg to improve arc, someone else to improve on a spinoff, or even for a new language.
1) proper phase-distinction between compilation and evaluation
- not possible with the way that macros are currently done
2) if not (1), then there's no excuse for not making macros first-class
- see how to in: http://arclanguage.com/item?id=842
3) modules (like everyone :)
4) Extend the concept of application to data to all data types by making it programmable in arc
- Currently you can do ((list 1 2 3) 0) and you'll get 1,
- why not allow this for all data-types by having a call-back into arc, such that arc-code can switch on the type.
5) Hygienic macros
6) Removal of 'nil
- 'nil does nothing more than '(), and often it breaks (e.g:
- when printing cyclic structures
- the fact that not all '()'s are replaced by 'nil). )
- Seriously, what's the need for 'nil? Just use '()
7) That's it for now, if you want unicode, but bleh
In general, I think the focus on conciseness, while a good thing, should not be the sole purpose. I think pg is slightly misguided on focusing solely on that. You can get conciseness, if your language is powerful enough, through the use of libraries. Small changes like fn and the way if works are easily done through the use of some macrology.
4 is crucial in my mind. You cannot let the language have more power than the programmer. Also, it lets you build more consistent programs instead of needing your own gets and sets all over the place. This would make it a huge win in decreasing tokens a program has to remember and grok about a new program they are reading.
I really like #4, but what other data types would it support? Arc doesn't currently have anything other than lists, hashtables, and primitives. Unless you mean specifying call and set operations for tagged data types...
For instance, if you look at arc.arc, you'll see the first two macros are defined without the use of the macro mac. Using
(set foo (annotate 'mac some-function))
This sets the type to a 'mac (or macro :)
So yes, I guess I do mean for tagged data-types, but also for any possible future 'native' types that it will come to support. Or even redefining it within the context of your application.
The way I solved this in the Wiki was to give tagged values (except macros) the same calling semantics as their non-tagged equivalents. Thus, you can represent arbitrary datatypes as functions, tagging them to keep track of their type but still calling them.
a) The ability to define within arc what happens when a list is
used as function, instead of it being predefined in scheme
b) The ability to do this for any type and custom types.
And potentially:
c) For annotated values to be able to do this per-object,
by storing an extra field in there that is the getter,
where the default value is the one for the type.
E.g: (annotate 'blabla value getter)
If there is no getter, then use the one for type 'blabla
LOOP. I know Paul thinks it was a mistake and I hated it (for its un-Lispyness) until I learned it, but now I will not use anything else for iteration. The funny thing is that loop gives us both shorter code and fewer parentheses, the goal of Arc! In a list processing language, what comes up more often iteration? OK, Arc is closer to Scheme in spirit so the answer to that is probably "Recursion!", but us CLers think using recursion to iterate just means your language is weak on iteration so recursion gets forced into an inappropriate role.
loop (in CL, this is) is a full-blown iteration DSL with its own tricky syntax to remember and everything, all in the name of making such code terser. The only time I use MAPCAR, CL's map, is in the rare case where I can just say:
(mapcar 'length my-strings)
That is shorter than:
(loop for s in my-strings collecting (length s))
loop has an initially clause, a finally clause, several kinds of ways to iterate and step, and has special keywords for different kinds of value accumulation. Like I said, it really is its own little iteration language that cuts waaay down on parentheses and otherwise makes for more terse and more efficient iteration.
Better string processing. I didn't want to just automatically adopt the existing regexp convention. But since there is no alternative yet, dealing with strings is awkward.
I think "better string processing" is below the level of writing a grammar, even a mini-grammar.
If you treat strings as lists of characters and have good pattern-matching, this problem mostly solves itself, without something as ugly as regular expressions. You have the rich set of list operations, function-based predicates and, with clause selection by pattern, that's mostly what you need.
Yes, I know strings-as-lists seems like a terribly unoptimal thing to do; but it's an optimization challenge (how to optimize a particular backing representation of lists so that certain kinds of operations--subset matching, tokenizing, etc.--happen efficiently?), not something to bake into the language.
I'd like to see a separate parser that can be programmatically modified. So people can use {} for macros, or define custom literals.
Javascript has this notation for hashes with symbol keys:
{a: 97, b: 98, c: 99}
Ruby has this notation for lists of strings:
%w(monday tuesday wednesday).
Ruby also has 5 different ways of writing a string literal, all of which are useful.
These things are not cs-research topics, but they make programming so much more pleasant.
Use of punctuation in all built-in functions, as well as the "coding guidelines" to reflect the function's return value:
(foo? bar) for functions that return boolean values
(foo! bar) for functions that modify the value of bar, and return it
(foo bar) for functions that don't return boolean values, and don't physically modify the value of (bar) [ie. everything else]
You may want to consider something non-traditional in Lisp circles: infix functions. Let me elaborate (and use the proposed new angle bracket syntax).
The "f:g" syntax is a nice idea for function composition, as is "~f" for negation, but the current syntax and semantics seem like a hack. What if we could do this instead:
(def f (x y) ...)
(infix f left)
<x f y> ; The compiler would transform this to (f x y)
<x f y f z> ; And this to (f (f x y) z) (evaluate from the left)
(infix f right)
<x f y f z> ; And now to (f x (f y z)) (evaluate from the right)
; Use in "normal" syntax:
(= foo <x f y>) ; --> (= foo (f x y))
; So, we could do this:
(infix + right)
(= foo <1 + 2>) ; Would evaluate/transform to (= foo (+ 1 2))
(= foo (+ 1 2 3 4)) ; And we could still use the ordinary function call syntax.
; Or maybe even:
(infix + associative)
(= foo <1 + 2 + 3 + 4>) ; Transforms to (+ 1 2 3 4) if the function is n-ary, or
; some arbitrary evaluation order if not.
; And now for the interesting bit.
(def : (f g) (fn (x) (f (g x)))))
(infix : right)
(map <f : g> list) ; What currently is (map f:g list)
; Why not extend the idea?
(def ~ (f) (fn (x) (not (f x))))
(unary ~)
(if (<~ predicate> arg) ...)
; No, that doesn't have any benefit over ((~ predicate) arg).
Problems:
- How do you define precedence levels? Once you are in this swamp, you can't get out. Maybe all infix functions would have the same precedence to side-step the issue.
- Is it possible to do with only the < > notation? But how would you parse the following without the additional information that + is infix?
<+ + + + +>
- Is it worth the two more characters and whitespace?
Precendence levels and such are a known problem and have a known solution. There are rather easy algorithms out there that deal with this. The only problem is that because you lack typing, you could end up with things that are not parsable:
This is a big one but a simple solution is possible in a first time. Since Arc is mainly for web apps, why not at least convert non-ascii characters into their url-encoding equivalents ? Arc is already converting those characters, but in a broken way : EUR instead of the euro symbol, for exemple.
Modules would be great, too. Well, nothing new here, anyway.
This is not a change, but a reiteration of a goal: keep it small, keep it fundamental. It would be wonderful if arc 1.0 turned out to be the 100-year, onion-free language, and all else occurred in libraries. pg wisely deferred the Unicode issue, and I hope he's actually doing that to demonstrate that it's not ultimately a language issue--and to give others a chance to provide a silver bullet(s) for all things character-ish. I chased Scheme through two major releases, until it went off the r6rs cliff. I want a tool for thinking about problems/applications, and I don't want to do yet another brain transplant every 3 years as the language grows more cruft. Oh, and a worthy thing to bundle with arc would be a test suite that implements every algorithm in TAOCP. Thank you:-)
People keep saying that it's unicode is a library issue, but it's not! It's a language issue!
If you move unicode to the libraries, you have to move the string type to the libraries too, assuming strings are lists of unicode characters. And if you move the string type to the library, how can you convert a symbol to a string?
That's not an option, so the alternative is that strings are just byte arrays, ignorant of their encoding, and you need libraries to find out what the length of a string is, and you're back in the tar pit where Ruby and PHP were in 1997 and where they pretty much still are today.
I've been down that road before. I know exactly where it ends.
I guess I'm enjoying thinking of characters/strings/whatever in the same way I think of a media stream. I don't reason about media streams in my general programming, but when I need to do so I use a specialized, application-specific library--the language isn't built with any preconceived notion of what music is, or an image, or video. I like a minimal (if even that) built-in notion of what char/text is. I like Arc (so far) and I do hope it avoids premature textualization for a very long time:-)
So far, I'm not too keen on = for setf. Mainly for the negative reason that I like = with its comparison of numbers meaning. Perhaps (! a 0) for assignment?
The other short names for commonly used operators I find a big win though. I've already copied a lot of them to my own pet Lisp - that's how much I like them.
I have to say I think regular expressions belong ghetto-ized into a library. If they are bonded with the language, and a language is a tool to think in, then they encourage the programmer to think all data is textual strings to be transformed into structured data (over and over) via the text-matching engine.
This is one of the most irritating legacies of Perl and no new language should make that mistake. I'm not saying regular expressions have no place, but it's a specific and special problem domain, not a general one.
If I had to vote, it would be to have Prolog/Erlang/Haskell-type pattern-matching. I'm not sure what pg means when he says it's good for writing append and that's it--as an Erlang programmer it seems like bread-and-butter to me.
Small idea: Since Arc does major surgery on traditional Lisp syntax quirks, I think it should reclaim < and > as parenthesis, rather than symbol characters. Instead of (< n 0) you'd write (lt n 0), and use < for something else.
i think what is needed most is a sane development environment. with a language like lisp, which is so dependent on the REPL, you need good editor integration. This is why I stick with CL: SLIME is that good.
I love python's 2 window environment but the functionality I'd like even more (and please point me to it if it already exists) would be the ability to dump the current REPL log to a file so that I could tinker completely in the REPL without constantly having to copy/paste to/from another file.
operator overloading!
I have implemented lazy lists, but they can not be used like normal lists.
I'd like to define (cdr my-lazy-list), so i can pass a lazy list to functions like keep and map1.
Maybe Arc needs a real type system first.
This would also allow things like a splice operator (mentioned here: http://arclanguage.org/item?id=540); if we had this (assuming that @x expands to (splice x)), you could write (let arglist '(4 5 6) (list 1 2 3 @arglist 7 8 9)) and get (1 2 3 4 5 6 7 8 9). This would actually be superior to apply in certain cases: (and x y @arglist) wouldn't have to evaluate y or the parts of arglist, whereas (apply and x y arglist) has the same effect in most cases, but does evaluate y, which can be problematic. I suppose one way to solve this would be to give values a "cer" (Current EnclosuRe) or some such; if lst were '(1 2 (a b) 3 4), then (cer x) = nil, (cer (car x)) = x, and (cer (car (list-ref 2))) = (list-ref 2). It's not clear what (cer (cdr x)) would be, but it would probably also be x.
Personally I'd still implement this as a macro form, such that we would have a (spliceable ...) form which would scan its contents for splice-macros. So the syntax would look approximately like:
(spliceable ...) would essentially be a generalization of the `() operator, except that ` will only scan for , while (spliceable ...) would scan for any splice-macros.
1) Built-in C-interface
2) Native efficient compiler
3) Regular expressions
4) Built-in object system like CLOS
5) All data types, like hash tables and (CLOS?) objects, have their own text representation, so they can be read by the reader and properly printed.
6) Arc should have not only defmacro, but Scheme's define-syntax (defsyntax) too.
7) More functions for string parsing and printing.