Actually, I apologize. My earlier statement about macros plus some turing-incomplete subset of arc being turing-complete makes no sense. There is no such thing as "macros on their own". Macros are just Arc code that gets evaluated before what we like to think of as "runtime". Arc macros sans the rest of arc are nothing. The "difference" here is not that macros are (or rather, macroexpansion is) turing-incomplete. The difference is that Arc delineates macroexpansion from normal evaluation, whereas Pure doesn't have a distinction; everything is term rewriting.
I have to admit that Clojure looks very interesting. Functional programming, concurrency with software transactional memory, access to a huge Java library, conciseness, etc. It's also a Lisp-1 like Arc w/ Common Lisp style macros, but it has namespaces. It doesn't have TCO due to JVM limitations and I don't think it has continuations, so an Arc-like web server might be more challenging.
The concurrency story is particularly interesting given the proliferation of cores and slowing of GHz.
The Clojure version of Norvig's spelling corrector appears to be one line shorter than Norvig's Python version:
I would really like to see an Arc version of that. I translated Norvig's Python version to Ruby easily, so I expect that someone fluent in Arc could do it easily.
The other Lisp versions I've seen (Scheme/Common Lisp) were quite a bit longer and less elegant IMO.
I'm not super excited about Clojure's dependence on the JVM, but on the other hand, that gives it a huge head start with libraries and state of the art VM/JIT technology.
Rich Hickey doesn't seem to have the star power of pg, but he is very involved in the language and community. He seems like more of a Matz or Guido which doesn't seem that bad - he seems quite sharp in his web cast presentations.
I could be way off, but it seems like it would be easier to add Arc innovations to Clojure than to add Clojure-like concurrency support to Arc.
Stop it with the meta-decisions - learning Arc will take up one thorough afternoon. It's worth it because you'll start seeing ways of writing code in any language that mimics features Arc has.
Neither. I've never done any large-scale coding in Scheme (I'm a Common Lisper) but it's more than just a hunch. The theory behind name capture is easy to understand. The only counter that I've heard is, "Well, it just doesn't seem to be a problem in practice." I believe that this is only because the Arc code and user base have not yet reached the scale where the problems manifest themselves.
But even if Paul is right and name capture is rarely if ever a problem in practice, punting on name capture seems to me fundamentally at odds with the stated goal of producing the 100-year language. At best it's an unnecessary burden on the programmer, and at worst it's a time bomb that could go off any time. Either way it doesn't make me optimistic that Arc is going to win in the long run.
If arc is continuing by momentum, it's a very different kind of momentum than the momentum languages like Java, Python and Perl survive on. They have momentum in that they have many large and complex libraries and programs written in them which are being actively used to the point where rewriting said software in another language is no small task; and hence most people are interested, not in making a new and better language, but in making the old language better.
Arc has none of this. The reason why it continues is because the people using it are enamored of it for its own sake, not because they depend on it. This is both a blessing and a curse. A blessing, because it means Arc is free to change and improve; a curse, because it means Arc is incomplete and a moving target.
I agree that the authors have failed to create a sense of community by mostly being absent. They do provide a bit of feedback, but it is minimal at best. It is clear that their interest in this project is not aligned with the current interests of the self appointed "user base".
However, in my opinion (and I'm a nobody so you may want to stop reading here), the arc core as well as some of the code that ships with it (news.arc, blog.arc, html.arc) rank amongst the best examples of working lisp code that highlight the power of lisp. news.arc is a great example of the use of macros and arc.arc is a great example of the power of embedded languages (feel free to point out better). To say nothing of the fact that this stuff is simply beautiful in a way I don't get from script-fu, elisp (slime being the one exception), edi weitz libraries, SICP etc. (it probl has something to do with how concise arc is).
So whatever. If more arc is thrown our way, I'll be happy to take it.
A cynical view of someone who has not participated in the arc community, and barley lurked longer then the time taken to sign up:
1) How long was it between you first hearing about Arc, and you seeing the first prototype? For me, it was a long time. Expect similar changes to happen along similar timeframes.
2) Arc is a Lisp: a language notorious for community fragmentation and trivially incompatible implementations. Without a strong, active leader, expect more of the same.
3) Arc isn't so much a prototype language, as a mini-fad / hype-based language. All things which survive find a purpose, Arc has rejected practicality as a goal, and anyone doing language research has probably already written their own Lisp dialect, so the market is probably limited to Hacker News readers. Activity on arclanguage.org probably spikes whenever it comes up on HN, and depending on the news, has a long or short trail off. I probably won't return until another interesting Arc headline reaches to front page.
Oh, Clojure is not quite completely immutable. Clojure has refs, and they can be mutated within the context of a 'dosync form. Kind of like the Haskell "do" syntax. It's more that Clojure defaults to immutability, and has special syntax to define portions that are imperative.
> Just in general - is there anything in arc which gives it a big edge to programmers when compared to clojure?
Mutation. It's one thing to allow functional programming. It's another thing to force it.
The only thing constant is change.
> * The read table is currently not accessible to user programs
Neither does Arc, although Anarki does allow redefining of 'ssyntax and 'ssexpand, which almost gives you the same thing.
IMO not giving access to the read table is a good thing. There are very subtle problems with this, starting with: the read table affects all code loaded after the readtable modification.
It affects them whether or not the code was written by you, and whether or not it was written with that readtable definition in mind.
This can cause unfortunate library breakage when two libraries try to modify the same readtable entry; the poor user is thus left at the mercy of which library loaded last.
In fact Arc-F has revoked Anarki's feature which allows 'ssyntax and 'ssexpand to be modified; redefine them all you want, Arc-F will use the built-in traditional 4 ssyntaxes : ~ ! .
HOWEVER, there are currently two reserved context metacommands which will eventually allow ssyntax redefinition at the level of the individual file: 'import-ssyntax and 'interface-ssyntax.
The important thing is that they are context-based, and because they are context-based, they are not global and they will (in general) affect only one loaded file.
> * Keywords are not Symbols
Unimportant - salt to taste.
> * Symbols are not storage locations (see Var)
> * immutable data
This is Will Fitzgerald. I wrote spec, but haven't been writing any arc code recently. I put Spec under the artistic license because that it what Arc used. I would be glad to offer a different license if that seems necessary.
If that's the case, we have a much larger problem. From the "copyright" file:
This software is copyright (c) Paul Graham and Robert Morris. Permission
to use it is granted under the Perl Foundations's Artistic License 2.0.
This is from arc2.tar (and thus Anarki as well). Thus if that's true about the Artistic License, all of Anarki is in violation. According to Wikipedia (http://en.wikipedia.org/wiki/Artistic_License), however, the Artistic License 2.0 is both free and open source---in fact, it's GPL compatible, and perl itself is dual-licensed under both of said licenses. Thus, I would think we would be OK.
It's a bit late/early and IANAL, but my reading of the Artistic License is that we are OK given section 4. Section 4 says that we can distribute modified versions of the source provided we document the differences and either: (a) make the modified version available to the original creator under the original license; (b) ensure that one can install both the modified and original versions separately and that the modified one has a different name; or (c) allow redistribution of the source under either this license or another free license. We're definitely doing (a) and (c) and we're probably doing (b), so if I understand this correctly, we should be in the clear.
Thoughts on this interpretation? This is much bigger than just one library, after all...
Please choose a more informative title for your question the next time: I suggest "Dumb question about quotation axioms" or "Is quote redundant with quasiquote?"
> Do you really think you're going to be calling a C++ function through an FFI in 100 years?
I think I will be dead in 100 years.
> I'm not sure if there is any good way to solve this dilemma
The only solution is to write libraries. Take some problem you think is generic enough for a library and write code to solve it. I've already written in Arc a library to download files through HTTP and a simple XML parser. They're not complete, but they're something. Talking about libraries' shortage won't solve the problem. We have to write code.
Actually, especially since Arc lacks a debugger, I find it very convenient to be able to just place a pr: or pr. in front of some symbol to get an expression printed out without the behavior changing.
I keep saying in this forum that we need to concentrate on language design and on writing libraries. I keep saying to myself that I should work on applications or at least on some useful library. I keep writing half-working implementations. Here's my second attempt. Arc on parrot.
i like how Lua is a language that "just works" (within reason of course.) i miss its awesometastic tables when using other languages. it's also rare in that it doesn't promote any particular paradigm. it lets you use what you feel is best. relatedly, its syntax is deceptively simple
anywho, i took a glance at the code. i know you're going for performance so maybe that affected the mechanisms you used, but here's some suggestions anyways:
* Lua's string concatenation operator is .., as in
return "str1" .. "str2"
* in tight code, local variables should be used anywhere possible. global variables require a table lookup, but local variables are on the stack (or register... one 'o thems.) so for example, you might want to have something like
local tinsert = table.insert
at the top or above the functions it's used in. it can't exactly JIT the table.insert because table lookup is dynamic (in both the tables you move around and the global environment)
* string.gsub might be faster than iterated replacement
* a key in a table can be anything, even functions and other tables, for the hex2int you can therefore have something like:
{ ["1"] = 1, ["2"] = 2, ... ["a"] = 10 }
i don't remember if the square brackets are necessary there
* as far as ipairs is concerned, a nil value signifies the end of a table, so if you have:
{ 1, 2, nil, 3, 4 }
ipairs will only iterate from 1 to 2. hence, depending on circumstances, you may be able to use this to make table splitting a bit faster
* table.concat might be useful somewheres
* a map-like function where on each iteration you return not just a value, but a key-value pair might be useful. eg pretending you don't care about performance, reverse could be written:
function reverse( a )
local len = #a
return tblmap( a, function( k, v )
return len-k+1, v
end )
end
it also makes mirroring tables easy. (again, ignoring performance. this is something you might use in the user code rather than in the library)
* one way i've set up bi-directional tables before is with __call. in that form, tbl[key] would access one way, and tbl(key) would access it in reverse. another option is something like tbl[1][key]/tbl[-1][key]. depending on how you're using the table, you can memoize in one of the directions and/or set up some sort of little database hidden behind that single table
* Lua doesn't have macros, but it does have first-class environments. among many other things this means it has anaphoric macro-like capabilities, except for the need to pass in expressions through closures or strings. take for example these lines from luarc.lua:
local sep = sep or ' '
local pattern = string.format ('[^%s]+', sep)
local f = string.gmatch (s, pattern)
local match = f()
using a function seq:
function seq( ... )
local that = nil
local original_env = getfenv( select( 1, ... ) )
for i = 1, select( '#', ... ) do
local fn = select( i, ... )
local new_env = { ["that"] = that }
-- have new environment inherit from old, otherwise
-- any global variables used in the fns will be unavailabe
setmetatable( new_env, { __index = original_env } )
setfenv( fn, new_env )
that = fn()
end
return that
end
or a shorter version if we have another function "with":
function seq( ... )
local that = nil
for i = 1, select( '#', ... ) do
local fn = select( i, ... )
that = with( { ["that"] = that }, fn )()
end
return that
end
we can write:
local match = seq(
function() return sep or " " end,
function() return string.format( "[^%s]+", that ) end,
function() return string.gmatch( s, that ) end,
function() return that() end )
this is an extremely questionable abstraction to say the least (note, none of this was tested,) but it's an example of some of the sorts of things you can do with first-class environments, which might help in betterizing the way the library is used. keep in mind that environments are tables, whose metatables may be set like any other tables, which need not be constrained to easy childish things like inheritance. eg:
do
local test_env = {}
local i = 0
setmetatable( test_env, { __index = function( tbl, var )
if var == "var" then
i = i + 1
return i
else
return _G[var]
end
end } )
setfenv( 0, test_env )
end
> print( var )
1
> print( var )
2
> print( var )
3
* finally, the horse's mouth is usually the best teacher:
No good reason I know of... The "is" roughly maps to comparators in other Lisps that are designed for "near constant time" comparisons, which is possible for symbols and numbers.
Strings may have gotten grandfathered into this behavior because they were thought of as similar to symbols.
Another possibility is that a Lisp string implementations could, in theory, use pre-computed hashes for comparisons, which WOULD allow near-constant comparisons, while slowing other operations.
To be clear, I didn't mean offense with the comment about learning new things (hence saying "I doubt it").
Moreover, my point was that the differences between releases are relatively easy to cope with -- yes, even for py3k. Sure, there are a hefty number of changes, but they mostly occur in a more "library" level (if you will) rather than at the base language, and much of it is just cleaning up inconsistencies. It's not as though the entire "16 years of Python" flies out the window with an overhaul like this. It'll be something of a pain in the ass to update code, but it won't make your collected knowledge obsolete, per se.
On a less contentious matter, Arc is also a tiny language. Its changes won't be all that drastic at this point. This is little consolation, as the aim is probably to build up a respectable library base. To that end, my overarching point is that you should learn Lisp in a more platonic sense (hardly a waste of time, break-prone versions aside). This can be done with pretty much any dialect, for the record. Libraries may become obsolete, but this sort of philosophical knowledge will not.
Whether this entails using Arc needn't be a grueling decision: it's a small, simple dialect (cf the responses in the aforementioned thread), and still a Lisp. The impression I get from the announcement is more of a heads-up "we reserve the right to..." thing. Besides which, changes to Arc entail, to a significant enough extent, code written in Arc itself. What better way to understand and play with the language than to read over the definition of 'trav (as a real example of a change between arc1 and arc2; http://arclanguage.org/item?id=2504)? Regardless, as Arc stands it's stable enough for learning, but this education has far more impact than being some sort of fleeting "woops, new version, guess I don't understand Lisp anymore." I focus on this idealized learning-experience viewpoint because that's the reason I tend to play with other languages: to expand my mind.
If, however, it comes down to consistency that you'd like for a Lisp in a production environment, well:
It's not a coincidence that we wrote a language for exploratory programming rather than the sort where an army of programmers builds a big, bureaucratic piece of software for a big, bureaucratic organization.
also noticed that the difference isn't that large as the racing car seemed to suggest. Chrome's javascript has about the same performance as Opera's right now. We'll see how it develops.
Hah. someome made ruby run on v8 and made a small benchmark of it. Interesting:
There are two great problems when learning Arc: the lack of documentation and the quite cryptic error messages. For these two reasons I would reccomend to know a little lisp before learning Arc. A really good book is Practical Common Lisp (http://www.gigamonkeys.com/book/). I suggest reading at least the first ten chapters.
First of all, this is a nice piece of work, rincewind- I'm going to have to think a while to decide on the merits of this approach for my personal use. I like the fiendish use of 'zap :-)
FYI- If any of you are confused by the extra 2 in rincewinds code, it is NOT one of the numbers to be added. The currying code just requires it because of the impedance mismatch between variable arguments and currying. The Haskell core doesn't have variable argument support, so this is not an issue in Haskell.
Keep in mind that "On Lisp" covers currying, whereas arc currently does not. I assume this is because pg is still trying to decide on a currying approach, whether it will be implicit currying, syntax-based currying, or function-based currying.
Here's an awesome variant based on your approach I'm probably going to try and implement soon, now that I've thought of it- Have a number in the function position (currently an error) initiate currying!
For example:
> (2 +)
#<procedure>
The two here, as in rincewind's code, means that the curried version of '+ should resolve to the result after two parameters are passed in.
> ((2 +) 7)
#<procedure>
> (((2 +) 7) 5)
12
> (map ((2 +) 7) '(1 2 3))
(8 9 10)
> (map (2.+ 7) '(1 2 3))
(8 9 10)
(these next ones assume my mods to the intrasymbol syntax are in place http://arclanguage.org/item?id=7644)
> (map 2.+.7 '(1 2 3))
(8 9 10)
(for those of you wondering why the "2" is necessary)
> (map 3.+.7 '(1 2 3))
(#<procedure> #<procedure> #<procedure>)
> (map [_ 100] (map 3.+.7 '(1 2 3)))
(108 109 110)
Note that the "integers in function position" concept could also work with implicit currying- In that case, "2.+" would simply convert '+ into the dual arity version of '+. The examples would stay the same- However, in that case the integer could of course be ommited for fixed arity functions.
Oops, yes, sorry. I just released a new version of the code and it had some new news.yc stuff in it that I hadn't adapted yet to arclanguage.org. Will fix.