Why are you complaining about def being redundant, instead of railing against let? Nobody will argue that let isn't redundant (although many will say that it's useful).
i see 'let' as the core operator, and 'with' as the one that is redundant. you're right though. i wonder if they couldn't be combined into just let. i don't see the difficulty in something like
but the intention isn't to merge everything for the sake of it, it's to simplify the surface "interface" for the programmer. fn/def and let/with are both things the programmer will use often
though in the case of let/with it may be more readable to keep the current setup since it saves the eyes the trouble of checking how many variables are bound, even though it's just a check for parens. i haven't programmed enough to really say
I agree with you that 'let and 'with are redundant (although I still disagree about 'fn and 'def).
But I think the difference between the 'let and 'with forms would be removed better simply by removing the implicit progn. This was brought up previously in http://arclanguage.org/item?id=3234 .
This would allow (let a 1 form) like the current 'let, or (let a 1 b 2 form) like the current 'with. For multiple statements you would need (let a 1 (do forms)), but pg already said how he liked that 'do highlighted non-functional code.
(fn name (...) ...) would be equivalent to the current (def name (...) ...), while (fn (...) ...) would be equivalent to the current (fn (...) ...). the intention isn't to replace the current form of fn, but to give it an optional parameter at the front so that both forms work
btw you can already use def in place of fn:
arc>((def fun (x) (+ x 1)) 3)
4
arc>fun
#<procedure: fun>
this version would be able to handle varargs, just not with the same notation as it currently uses. i mentioned (fn name ('() . args) ...) as a possibility, but i think something better would be
(fn name ((v args)) ...)
sort of like the o notation for optional parameters. (fn name (() . args) ...) isn't that bad either. varargs of the form:
(fn name (a1 . rest) ...)
would be unchanged. the only requirement necessary is that args always be in parens, and the only place where that currently conflicts is with full varargs
Even if your intention is to just add an optional parameter to 'fn, you still have to implement it in ac.scm. You can't even redefine it in Arc because it is dealt with specially in 'ac.
I still think there is a logical distinction between defining a function literal, and assigning that function to a variable. One should be implemented as an axiom, and the other really should be implemented in the core language. Pulling the latter into the former just doesn't make sense.
As for varargs notations, the only one I like is (fn ((v args)) ...). Because (fn args ...) is basically a case of destructuring, the (fn (() . args) just doesn't make sense. In fact that notation actually does work in the current Arc, it just throws away the first argument. (Which makes sense if you think through what that code is actually saying.)
But even considering the possible alternatives, I still think the current syntax is the most elegant.
how about if i said: "make the name parameter of def be optional and rename it fn, and rename the current fn to axiomfn" ? we wouldn't be touching the axioms in that case except with a rename
i'm just saying there should be a functionality like that. how the internals are structured to meet that end isn't as significant so long as we're not butchering things. and i'm a big fan of the concise axiomatic approach. it's the reason i use Lua instead of Ruby. and that's why i think merging def and fn would be better -- because to the programmer it would be simpler, even though underneath it may need a bit more sophistication to handle the special case of full varargs
There's still this logical distinction between creating an anonymous function and assigning it to a variable, and I don't think merging 'def and 'fn captures this distinction.
Note the differences in the use of 'def and 'fn. 'fn is used most frequently as a local function, while 'def is a global definition. If you provide a named form of 'fn that assigns a global name, then it will be really tempting to misuse that form in a local context where you really shouldn't be messing with global bindings.
And why should 'def get a pulled into 'fn but not 'afn or 'rfn? In some ways, those make much more sense than the integrated 'def form, but note that all of those were made explicitly different in the core language. We want to make it explicit how we are handling the naming (and scoping) of our functions. Thus the four or more different ways to define a function.
>
> There's still this logical distinction between creating an anonymous function and assigning it to a variable, and I don't think merging 'def and 'fn captures this distinction.
(fn name (arg) ...) and (fn (arg) ...) seem pretty distinct to me
>
>Note the differences in the use of 'def and 'fn. 'fn is used most frequently as a local function, while 'def is a global definition. If you provide a named form of 'fn that assigns a global name, then it will be really tempting to misuse that form in a local context where you really shouldn't be messing with global bindings.
if fn is being used in a local context they probably won't need to give it a name
for afn and rfn i was actually thinking they could be merged as afn:
then there would only be two function-creating thingies: fn and afn. i stated elsewhere that i see the necessity for 'and' and 'aand'. fn and afn are substantially different in that the variable environments of their evaluated bodies are different, as well as producing different classes of functions
truthfully it would be nice if they could all be merged into one, and they could be, except for example 'self' would always be pre-set in lambdas which would be (conceptually) annoying. that would be tolerable though
> > There's still this logical distinction between creating an anonymous function and assigning it to a variable, and I don't think merging 'def and 'fn captures this distinction.
> (fn name (arg) ...) and (fn (arg) ...) seem pretty distinct to me
Think about the difference between (car x) and (scar x). Those are two distinct cases (assignment and retrieval), we wouldn't want to create a single 'car function that would do assignment if given an optional parameter.
(_car x) => (car x)
(_car x y) => (scar x y)
Because these are two entirely different concepts. Similarly the assignment of a function to a variable is distinct from creating that function. In my opinion, merely creating an optional parameter for it in 'fn doesn't capture that distinction.
> if fn is being used in a local context they probably won't need to give it a name
Then why do 'afn and 'rfn exist?
> fn and afn are substantially different in that the variable environments of their evaluated bodies are different
And thus 'fn and 'def are different in that 'fn creates no binding and 'def creates a global binding.
fn and def both create functions. in one case we donate money. in the other we donate money and get a star with our name on it. in both cases we've donated money. car vs scar would be like donating money vs robbing a bank
>> if fn is being used in a local context they probably won't need to give it a name
> Then why do 'afn and 'rfn exist?
to create self-referencing functions
> And thus 'fn and 'def are different in that 'fn creates no binding and 'def creates a global binding.
yes, and afn can create a different class of functions from fn. almkglor had a good point about requiring gymnastics in some cases with a God Fn. those sorts of issues wouldn't happen with (fn,def) -> fn and (afn,rfn) -> afn, except for the full vararg thing
if we use software structure/logic as the only metric, sure, they're all very different, but then we'll lose sight of the way the end user sees all these things
> fn and def both create functions. in one case we donate money. in the other we donate money and get a star with our name on it.
No, the star gets permanently placed in the floor of the quad outside the central office. As opposed to 'afn and 'rfn where we get to keep the star.
Admittedly my 'car and 'scar example wasn't accurate. Let me try again. Say we have a version of 'table called 'ntable that is just like table except it assigns the new table to a global variable. Now suppose we redefine 'table to take an optional parameter, in which case it acts like 'ntable. But all this does is confuse the purpose of 'table, which is to create a table, and 'ntable which creates a table in the global namespace. The overloading here doesn't help shorten or clarify code. So we remove one function from the global namespace. So what?
Unlike in real life, the presence or absence of the star really does matter here, enough that the two shouldn't be condensed into a single operation.
>>> if fn is being used in a local context they probably won't need to give it a name
>> Then why do 'afn and 'rfn exist?
> to create self-referencing functions
Exactly! So you can use the function in more places than just the return value. 'def is similar in that the name allows it to be referenced elsewhere (or inside the definition), and this is totally different from the one-shot usage that 'fn gets most of the time.
> if we use software structure/logic as the only metric, sure, they're all very different, but then we'll lose sight of the way the end user sees all these things
I think we are making very different assumptions about the way the user thinks about 'fn and 'def. When I see 'fn, I think local/anonymous function. When I see 'def on the other hand, I think global/named function (and yes, where the name goes is an important issue, even to an end user). I don't think it is a simplification of this model to merge the two operations.
when i see fn, i see "fundamental function-creating function." i see def as "(= name (fundamental function-creating function))." i said elsewhere that i can see how someone familiar with lisp would see fn and def as essentially lambda and set, and
(lambda name (arg) ...)
would definitely be odd. but the name isn't "lambda," it's "fn." and i conceive that as "function," which has no semantic bias to locality or scope
So no. I don't think this puritan attempt at merging ideas is a good one.
"All processes are impermanent ... All processes are afflicted ... All phenomena are not ‘Self’; when this is seen with knowledge, one is freed from the illusion of affliction. This is the pathway to purity."
i wonder how we would refer to the different selves within the inner function without explicitly setting something in the outer one or using rfn. a let around the inner function isn't that bad. in any case, good catch
with the requirement that the parameters always be in parens, that would be an error
[edit]the inspiration for giving fn an optional parameter is that def is redundant. i don't think it would be strange at all. except for full-vararg cases, a straightforward 'def -> fn' search & replace would be sufficient
"with the requirement that the parameters always be in parens, that would be an error" : not really, because without parameters, you would have '() which is automatically translated to nil. You could do (fn (nil) nil), however, but I don't like the look of it. Not enough consistent, I guess (and a list containing the empty list to denote the empty list does not look right to me).
well, as a special form i don't think it would have trouble differentiating between () and nil. () seems to be different from '() even though (is () '()) is t
'() is (quote ()) while () is (). You won't notice the difference if you pass both to a normal function but you might if you passed both to a macro or special form.
While superficially (fn (. args) ...) may look decent, it is entirely inconsistent with the Arc reader. Function definitions have to be entirely representable in lists, and the example you list above is not a valid list. Not only would this break the current Arc, but you would have to reimplement the entire reader that Arc currently borrows from Scheme. Not to mention that you would have to define what it even means to have a cons with no 'car field.
hehe sure you can borrow that part. i don't know any python so you'd be burning the null brain. the indentation sensitivity is actually the reason i never got interested in that language. it's probably just the fact that things aren't explicitly closed -- something i wouldn't mind in lisp but feels like standing on a plank in the middle of the ocean in a structured language like python, probably due to the mixture of symbolic and visual notation which totally weirds me out man
though i'm sure it's really just an exaggeration in my mind due to only hearing about that aspect of the language, and i probably would have gotten used to it easily enough
the nice thing about using multiple newlines as markers for new forms is we don't have to worry about indentation
those examples i gave for exterior symbols are just to show the sorts of things that could be possible. the 4 * notation for example would be completely out of place in Arc. (it's a notation that one of my LOGO languages uses.) the only example of those that i think is nifty is the | notation
though that piping could have some interesting uses. basically it lets you write functional statements in directed form rather than nested
(def a (x) (+ x 1))
(def b (x) (* x 2))
((a => b) 3) -> ((fn (x) (* (+ x 1) 2)) 3)
3 => + <= 2 -> 5 or [+ 5 _]
a b => + => [* _ 3] => [/ 1 _] => sqrt -> (fn (a b)
(sqrt (/ 1 (* (+ a b) 3))))
with the "implied underscore" form of [...]:
a b => + => [* 3] => [/ 1] => sqrt
sqrt <= [/ 1] <= [* 3] <= + <= a b
args within nodes:
a => [* 3] b => + -> (fn (a b)
(+ (* a 3) b))
so it's basically composition with some things to make it more general
i wonder what tomfoolery could be done by using the list as the format for the directed graph
anyways, i don't really know what i'm talking about wrt piping, and i don't find the notation very handsome. though it just occured to me that we can already just make a 'pipe' macro/function that would allow us to write things close enough to that notation. programmable programming language indubitably
and again, these are just heuristics for the most part. maybe with the small spark of these examples, other people can come up with some useful notations, or more useful sparks that may eventually lead to some :)
Oh, I didn't see your note about multiple newlines separating forms. That is an interesting approach, and removes the need for meaningful whitespace. But I have to wonder if it might get a little tiresome at the repl, since you would always have to add an extra newline to all your one line forms (unless you reverted to outer parens again).
And I now see what piping does, although I agree with you that the notation doesn't look very nice.
Perhaps external symbols could be used to do infix math? Although that might make it difficult to distinguish from when you want to do (for example):
yea for the REPL i'm not exactly sure. i think it would have to be limited to 1-liners. for multiple lines then you'd have to use outer parens. where it would get wack is, say, copy & pasting code into the REPL, and if that code isn't using outer parens, how would it be parsed? it wouldn't be a problem if it was pasted and read in a 'chunk', but i think in my console it's pasted line-by-line or parsed line-by-line as if manually input. i'm not sure. hmmm. we might have to say "use outer parens if you're going to paste the code into the REPL"
however, that's the behavior as it is now in the scheme REPL. a custom Arc REPL would be able to handle things like pastes appropriately (maybe. i'm not familiar with console mechanics, but even if it wasn't 'purely' possible, a timer could be used so that if the delta between the last and currently entered lines is less than X, then it would be safe to assume the sequence was pasted)
for infix math it might be best to have a marker that says "the following form is infix"
#( (3 + 2) / 8 ) -> (/ (+ 3 2) 8 )
some other thoughts are notations for pattern matching/RE's
I'm really uncomfortable about making the repl and file readers differ, especially to the point of being incompatible.
About a custom console, I am not sure there is any reliable way to detect when a user has pasted code into the console. (That sort of thing is probably very OS dependent.)
Even if you did set up a timer like you propose, what would it do if the user recalled some lines from a multiline entry in history? I think most consoles would only pull up one line of history at a time, so the user would have to go through each line and enter it individually. And the timing would be such as to be indistinguishable from direct input from the user.
As for infix math, if you created an infix marker like
nice! well i know you didn't ask for cosmetic tips, but i think the titles are (slightly) too small/underemphasized (or other things are slightly overemphasized)
Thanks for the tip! I'l add that to the to-investigate list. I am trying to cram as much info into the page as possible, and it is hard to hit the right balance.
for multiple lists, the lists would all go first and the function last. i don't see why this is a problem
as for what makes sense, that gets into SVO vs VOS etc and i don't think it is significant in programming. i know i don't read programs as if they were english. the most important portion of the expression is the word "map." once i see that word, i want to know what the function is and what the list(s) are because that's all that is relevant. conceptually, it doesn't matter what order they're in, and neither order makes more "sense." not for me at least
[edit]i've used map in both orders (in functional languages that don't have map, i make my own,) and again, i haven't noticed a difference besides one of readability
Since most languages are SOV and SVO, Lisp already does not read like most human languages. Lisp is either VSO or VOS (depending on how it is written); the verb (function) comes first. However, the English or math-speak phrase here is "map f onto x". It is not "map x through f" or "map x by f;" neither of these is ever used. Set-builder notation writes { x/2 : x \in Z }; this is, in many ways, what map is based on, and it puts the function before the list. I find (map xs -) to be far less readable than (map - xs), not to mention (map xs ys +)vs.(map + xs ys). Furthermore, this way of writing it mimics function application: (car x) becomes (map car xs), and so on.
There is a slight gain in indentation-based readability with your version, but I'm not convinced it's substantial enough to warrant the less-readable, less-regular, less-common inversion to be the standard; at most, a provided variant, but probably something that one should define oneself.
And for what it's worth, if the function is particularly long, I usually write
i see your point about it mimicking function application, and that seems to me the reason if there was any for why that order was chosen
> I find (map xs -) to be far less readable than (map - xs), not to mention (map xs ys +) vs. (map + xs ys)
well, if the function is named, the order isn't as significant. i personally don't see a difference in either of those pairs, but compare (map + xs ys) to a version where + is a lambda, and likewise with (map xs ys +)
i wonder which usage is more common: map with a named function, or map with a lambda. i'm sure it is the lambda usage, but i haven't programmed much in lisp. it would be interesting to analyze some code and see some numbers
(map betas gammas
[foo (bar _)
(frob nitz)
(xyzzy (quux _ _)
alpha)])
(map betas gammas (fn (a b)
(foo (bar a)
(frob nitz)
(xyzzy (quux b a)
alpha))))
also, it could be made to accept any order. though i don't see a reason for that besides serving as a model for other functions w/ respect to the implied underscore notation
Clearly, readability is largely personal preference. As I said, I like the syntax of your version for lengthy anonymous functions. But I don't see it as a huge net win. It might, on the other hand, be nice to have a mapover macro which turned
(mapover as bs ... ys zs (a b ... y z)
(do-stuff a b ... y z))
into
(map (fn (a b ... y z) (do-stuff a b ... y z))
as bs ... ys zs)
which might be clearer in the really-lengthy-function case. Here it is (lightly tested):
(mac mapover (arg1 arg2 arg3 . arg4+)
" The last two arguments to mapover are treated as the parameter list and body
of a function `f', which is then applied to each element of the given
sequences (the other, previous arguments) in turn; a list consisting of the
results of this function is returned. In short, this is equivalent to
(map f arg1 arg2 ... argN), where `f' is the aforementioned constructed
function.
Note that there is *not* an implicit `do' around the body of the function;
if there were, it would be impossible to tell where the lists ended and the
function began.
See also [[map]] [[each]]. "
(withs (args (apply list arg1 arg2 arg3 arg4+)
body (last args)
parms (car:cut args -2 -1) ; 2nd to last
lists (cut args 0 -2))
`(map (fn ,parms ,body) ,@lists)))
Note that it's limited in that there is no implicit do around the body of the function; since it can take any number of lists, there would be no way to tell where they stopped and the function began. Also note that this may well rely on the Anarki, but I'm not 100% sure if it does.
(And I'm sure someone will tell me that CL's loop can do this in 3... 2... 1...)
Overall, I do think that (map f xs) is cleaner in the named-, short-, and moderately-long-function cases, but it's always possible to write an inversion if you think differently. But what do you think of mapover as another approach?
i wouldn't mind that, though it would probably be considered repetitive with map, and the first thing i would do would be (= map mapover) [edit]oops, macros aren't first class... yet
ya'll confuse me wit dem macros @_@. i made my own function called nap (the other option was pam, which rolls off the tongue with an undesirable cooking-spray sort of feel)
Note that mapover is less general than map. It's explicitly designed for the "long anonymous function" case, so you cannot do
(mapover '(1 2 3) '(4 5 6) +)
; instead, you must do
(mapover '(1 2 3) '(4 5 6) (x y)
(+ x y))
. This is why it is a macro: it needs to treat its last two arguments as part of a function body, so it cannot evaluate them. As a macro, it can package them up and put them in a function, which can be passed to map along with the lists.
nap is a clever function, but will oddly allow you to write (map '(1 2 3) < > '(4 5 6)), which is meaningless. Nevertheless, It's not a terrible idea.
true, i'm not sure why it escaped my mind. probably for a couple reasons. i use Vim, which i'm very happy with and whose editing power i'm not going to be sacrificing anytime soon. i was speaking from the perspective of a common beginner. and also i just didn't like DrScheme when i opened it up. its UI and graphics could use a lot of work
however that's just nitpicky, and DrScheme fits what beginners need. how reasonable would writing an arc language thing for it be? still, if someone had to choose between creating a DrScheme language thing and an Eclipse plugin, i think Eclipse would be the best choice
i'm sure it is very good, i was just talking about the graphical aspects of it. maybe you're referring to my mention of Vim, but i wasn't comparing. Vim is IDE-wise completely lacking, but that's something that doesn't bother me, especially due to its superior-to-all-others editing model. and the reason i say Eclipse would be a better choice is just because it's substantially prettier and more familiar
For editing text Vi and Emacs are preferred by quite a few hackers.
However for editing Scheme, DrScheme is the best choice.
Not because it is "graphical", but simply because it was designed with one purpose only: writing Scheme code.
It has all the standard stuff (such as: to show the documentation on an identifier just press F1), but
it also contain the Scheme specific stuff, that you
won't notice until you try some advanced Scheming.
Among the most impressive features is how precise the error messages are reported. This goes for both standard errors, but not the least when macros are involved. If you compare the error reports you get from a few errorneous macros in DrScheme and in a standard Scheme implementation, you'll see that you can save a lot of time if you use DrScheme.
And while at, don't forget try the macro stepper...
i like drscheme in general. there is one problem with it that made it simply unusable by me. when you hit run in drscheme to reevaluate your definitions, it obliterates my entire history in the REPL. i need to have that stuff around to retry expressions.
i don't know the technical terms, but probably one of the things that gives Lua its speed is that if you have multiple strings in the program that are the same, the VM assigns them the same pointer. string comparisons are therefore trivial and i imagine this mechanism would make table lookup very direct
That's what Arc's symbols are for. Generally speaking, you should be keying your tables with symbols for exactly that reason: every 'a is at the same place in memory, but every "a" is not (which allows mutable strings).
You had me worried, but I'm pretty sure there's absolutely no problem with using strings as the keys for tables.
The MzScheme documentation says: make-hash-table ... 'equal -- creates a hash table that compares keys using equal? instead of eq? (needed, for example, when using strings as keys).
Checking ac.scm, sure enough:
(xdef 'table (lambda () (make-hash-table 'equal))
Likewise, the "is" operation in Arc uses MzScheme's string=? . (That's a statement, not a question :-) So string comparison works, although in O(n) time.
Net net: strings are okay for comparison and table keys in Arc.
I wasn't saying that they weren't usable, just that they were, in fact, slower; that's what you observed. Symbol comparison is O(1) time, whereas string comparison is O(n) time. That's all.
I would rather have immutable strings + unification of symbols and strings.
- Any string could have an associated value, like symbols today.
- "foo", 'foo and (quote foo) would be the same object (you would allow Lisp-style prepend-quoting of non-whitespace strings for convenience).
- foo, (unquote "foo") or (unquote 'foo) would then be used to evaluate, so even non-whitespace strings like "bar baz" could act as symbols (but with less convenience, of course, since you would have to use the unquote form to get them evaluated).
- Since such a unified string/symbol would also act as a perfectly good key/value pair, a simple list of such strings will in effect act as a string-keyed hashtable (since duplicate strings in the list would be the same immutable key), and can be used wherever you need symbol tables (e.g. for lexical environments). In fact, string-keyed hash tables would be a subtype of any-sort-of-key hashtables, and probably used much more.
. And by (unquote "foo"), do you mean (eval "foo")? Or do you mean `,"foo"? The latter makes more sense here.
At any rate, I'm not convinced that this is actually a benefit. Strings and symbols are logically distinct things. Strings are used when you want to know what they say, symbols when you want to know what they are. Unifying them doesn't seem to add anything, and you lose mutability (which, though unfunctional, can be quite useful).
> Strings and symbols are logically distinct things. Strings are used when you want to know what they say, symbols when you want to know what they are.
Fine. But this breaks down anyway when you force people to use (immutable) symbols instead of strings for efficient allocation. When using symbols as keys in hashtables, you do not "want to know what they are", you "want to know what they say".
And unification would possibly have good consequences for simplifying macros and code-as-data (especially if characters are also just strings of length 1). Most code fragments would then literally be strings (most notable exceptions would be numeric literals, list literals and the like).
Actually, in a hash table, I usually don't care what the key says, any more than I care about the name of the variable used to store an integer. I care about it for code readability, but I'm usually not concerned about getting a rogue key (where I do care what it says). In that case, I would either use string keys or (coerce input 'sym).
I'm not convinced that characters being strings of length one is a good idea... it seems like the "character" is actually a useful concept. But I don't have a huge opinion about this.
Code fragments would still be lists, actually: most code involves at least one function application, and that's a list structure. Only the degenerate case of 'var would be a string.
> Actually, in a hash table, I usually don't care what the key says, any more than I care about the name of the variable used to store an integer.
That's fine again, but my point is just that by using symbols as keys in hashtables, you never care about the value part of that symbol (you just need an immutable key); you're not using the symbol "as intended", for value storage.
> most code involves at least one function application, and that's a list structure.
Yep. But in the case where that function application does not contain another function application (or list literal) in any of its argument positions, we would, with my proposal, be talking about a list of strings, which could then again be seen as a string-keyed hash table...
Symbols are not "intended" for value storage, symbols happen to be used for value storage. Symbols have exactly the same properties as, say, named constants in C, and thus fit in the same niche. They also have the properties of variable names, and so fit in that niche too. Symbols are a generally useful datatype, and they are no more intended for just "value storage" than cons cells are intended for just list construction.
A list of strings is still a list, which is not what you said; right now, it's a list of symbols, and I don't see the benefit of a list of strings over a list of symbols.
Really, I think most people are confused by the boundary between interface and implementation.
It's possible to have a mutable string interface built on an immutable string implementation. Just add indirection! We can get the best of both worlds: immutability of actual strings optimizes comparison of strings, while the pseudo-mutable strings allow you to pass data across functions by a method other than returning a value (i.e. mutating the string).
That's a good point. However, it leaves open the question of what "a new string" creates. One can build either one on top of something else (e.g. immutable strings on top of symbols [though one could argue that that's what symbols are, I suppose]), so the real question (it seems to me) is what the default should be.
This is where "code is spec" breaks down, again ^^; \/
I suppose if the user uses symbol syntax, it's an immutable string, while if the user uses "string syntax", it's a mutable string. Interface, anyone? ^^
edit: typical lisps implement symbols as references to mutable strings; some newer ones implement mutable strings as references to immutable strings, with symbols referring also to immutable strings.
This isn't so much code is spec, though: Arc only has mutable strings and symbols. You could consider symbols immutable strings, but they exist to fill a different niche.[1] If mutable and immutable strings were created, then the code-spec would have to deal with this; I think it would be capable of doing so.
I'm not so concerned with how Lisps represent symbols and (mutable) strings as long as (1) my strings can be modified, and (2) comparing symbols takes constant time. If it's the Lisp interpreter protecting the string-representing-the-symbol, so be it; that doesn't affect me as a Lisp programmer.
[1]: Although if I recall, Ruby 2.0 will make its Symbols "frozen" (immutabilified) Strings, thus allowing things like regex matching on Symbols. This might be an interesting approach...