There isn't. I was considering writing something like quicklisp for Arc, but the downside is that with no module system, recursive loading of dependencies will break two different pieces of code that depend on different versions of a library.
We should probably at least make a wiki page with libraries, though.
Historically speaking, Arc was put forward as a language for experimentation. If you want to package up code in a squeaky clean format for others to use who don't care about implementation details, you're doing something other than experimentation; you're publishing a product people would supposedly rely on.
Moreover. not even the Arc language is stable (or so this website has always said). If it encouraged people to package up their stable code, that would prove deceptive once the whole foundation fell away. This happened to Anarki when Arc 3 was released; the community had built up lots of Arc 2 code, and not much of that was ported to work on Arc 3.
However, Arc is not without module systems. The first thing I did in Arc was build some namespace macros with explicit imports and exports for my own benefit (Lathe). Andrew Wilcox made "the hackinator" which I think could download Arc programs from a Web-hosted manifest file, most often from GitHub.
More recently, Arc/Nu is an Arc implementation with rich support for first-class namespaces, along with several utilities for modular loading of Arc code that's written in the usual sequence-of-commands style. Arc/Nu is probably the best platform to publish stable modules for, since it can load alternate namespaces to achieve backwards compatibility.
I saw a presentation by Stefan Karpinski, one of the Julia creators. It's a very interesting language that I'm sad to report that I have absolutely no reason to use. Unfortunately, I don't do any kind of mathematical computing.
I'm not too sad, because I do prefer prefix languages, but if I had to do something in that realm, I would definitely try out Julia.
I wonder if the author thinks the traditional if statement is flawed in the same way.
Most of my if blocks _are_ a single s-expression for each branch. I guess it depends whether you prefer to write functionally or imperatively. pg stated somewhere that, if the choice is forced, he will make the functional version of the code shorter, rather than the imperative version of code.
I also find it very interesting that the author doesn't indent the code at all. I hope it's just to make the point in this article, and now how ey normally writes code.
I also find it very interesting that the author doesn't indent the code at all.
I have a feeling (hope) that that was unintentional---like the blog formatting is just eating whitespace or something. Also, the first "Arc if" has a missing right parenthesis and uses = for comparison. :)
I was hoping other people would investigate their code! :) My personal arc directory filtered by hand for completeness (I have a lot of half-finished projects lying around), plus some stock Arc stuff I forgot in the last post:
You're right about blocks, so let's just focus on single-line 'if's. I can't think of a single non-lisp that allows adjacent condition and action without an intervening token. C and Java require parens, Go and Perl require curlies, Python requires the colon, Ruby requires a newline or semi-colon.
if (condition) action; # C, Java
if condition { action; } # Go, Perl
if condition: action # Python
if condition; action; end # Ruby
The 'else' token is a further separator. Never in these languages will you ever have two either-or expressions side by side. They're separated by either 'else' or 'else if' or 'elif'. Perl and Ruby even sometimes use 'if' as a separator.
action if condition # Perl, Ruby
Am I missing any counter-examples? I think the biggest error in this article is to blame Arc, when it's just extending the logic all lisps have always had. If you permit such adjacencies as (if condition action) and (cond ((condition action))), then Arc's choices don't seem so unreasonable.
(Though Clojure's mixture of Arc 'if' and optional types is a whole new level of unholiness.)
"Though Clojure's mixture of Arc 'if' and optional types is a whole new level of unholiness."
Is this what you meant instead? "Though Clojure's 'let', which mixes Arc's 'withs' with type hints, is a whole new level of unholiness."
Even so, I think Clojure's 'let doesn't actually have any[1] added complexity when it comes to type hints. It might look like the list is bunched into groups of either two or three depending on whether a type hint is present...
(let [^String x "x string"
y 2]
(body-goes-here))
...but that's not a quality of 'let. That's a quality of the ^ syntax. An occurrence of ^ consumes the next two s-expressions, just like ' consumes the next one s-expression:
So the bindings of a 'let are consistently bunched into groups of two s-expressions, just like Arc's 'withs.
[1] Of course, the type hints are actually used for optimization at some point, so the complexity of parsing them has to go somewhere. This is exactly as complex as destructuring: Arc's 'withs syntax supports destructuring, but it doesn't need special-case destructuring logic because it just translates down to 'fn. As it happens, Clojure's 'let syntax also supports destructuring, and it probably uses the same general technique.
Yes! Turns out I didn't notice the switch from if to let.
I have to say, though, I have no sympathy for the argument that it's still groups of two s-expressions. As a reader it's still more onerous to have to mentally group:
^a b
compared to:
'a
So the presence or absence of parsing complexity feels irrelevant.
I can't think of a single non-lisp that allows adjacent condition and action without an intervening token.
I mean, isn't this requirement mostly because those languages are infix anyway? Parsing gets easier with explicit ways of separating things. I could easily imagine shift/reduce conflicts or what-have-you cropping up when you try to eliminate the requirement for, say, parentheses around conditionals in Java.
For example, in some hypothetical infix language that doesn't require conditional separators (parens, braces, then, etc.), would
if x==10-x ...
be
if (x == 10) -x ... // maybe this language doesn't use the "return" keyword,
// so the conditional is returning negative ten.
or
if (x == (10 - x)) ...
?
Because Lisps use s-expressions, "intervening tokens" (per se) are unnecessary. As you say, Arc's choices don't seem so unreasonable, considering that.
Yeah, that's a good point. Non-lisps use keywords and punctuation for readability and to make parsing tractable, and the two reasons are often hard to separate in any single design decision.
To summarize my position: I have some sympathy for the specific argument that multi-branch 'if's are harder to read in lisp than in traditional languages[1]. But this affects any arrangement of parens, whether traditional 'cond' or arc 'if'.
To repeat my comments from earlier (http://arclanguage.org/item?id=17074), calling 'join on lists is not quite the same as calling '+ on the same lists. The results are 'iso to each other, but not 'is .
More confusingly, it also changes the cons cells inside the lists:
arc> (= l (list (list 1)))
((1))
arc> (is (car (+ l)) (car (join l)))
nil
arc> (iso (car (+ l)) (car (join l)))
t
>Is there also a function for adding two hash tables?
Hash tables are missing a lot of helper functions, yes. There's no built-in way (that I know of) of combining them, although writing one isn't hard -- you just have to decide what to do about collisions. I just took a few minutes and wrote one up. I deal with collisions by letting the earlier hash win. My code:
Note: somehow, I broke 'assert-same. Not sure why. I'll fix it later today; I don't have the time right now to make sure it's done properly. If you pull the default branch of my repo, and these tests don't work, bug me until I fix it.
Ugh, I'm past the edit time. Of course one wouldn't expect `(is (+ l) (join l))` to be 't, because that's the definition of 'is. (even `(is (list 1) (list 1))` returns 'nil). But the second part is bad, I think: the elements inside l should stay the same.
I blame not really hacking in Arc much lately. I haven't really been up for programming much of anything.
"But the second part is bad, I think: the elements inside l should stay the same."
I agree. I really think this is a simple coding mistake in ac.scm. I talk more about this here, a reply to that thread you linked to: http://arclanguage.org/item?id=17114
I completely forgot about this difference between + and 'join after that thread, and I don't remember encountering it before that either. How fitting it's a testing framework that brings this bug to the surface! :-p
While I don't have a Windows machine to test on now, I had gotten Arc working quite easily before on one (I was not running HN, which would exercise more code paths than were executed). Try it and see what happens.
Alternately, if this is an existing project, someone at the office should know how to get it working. If you try it and something breaks, let us know and we can help out!
I have a Windows 8.1 machine now, and it's still the same process. :)
The biggest pain was when I was trying to download the Anarki GitHub repo in the first place. I've been cloning GitHub repos over SSH URLs using Cygwin's build of Git, and setting this up on Windows 8 was a bit surprising: http://stackoverflow.com/questions/9561759/why-cannot-chmod-...
Nowadays, there are probably easier ways to get Anarki on Windows, like using GitHub for Windows or just using the GitHub website's "Download ZIP" button.
What are you looking to do with it such that you can't install it yourself? If you want to do anything with it, you'll want to be knowledgeable enough to install it.
Downvoting of comments already exists in the code; each user is prevented from downvoting until that user accumulates enough karma. I don't have the source in front of me, but you could set the threshold for that to 1, so users would be able to downvote comments as soon as they register.
Downvoting submissions is another story. As far as I know, HN has never let users downvote submissions, so you'd have to build in that scaffolding yourself.
Ugh, that's awful. I want to print out error messages that, in part, can be copied and pasted into the REPL. Right now, since double quotes are escaped, I'm printing single quotes, which obviously don't work. (https://bitbucket.org/zck/unit-test.arc/issue/40/make-error-...)
I guess this is a reason to switch to Anarki. But even that doesn't help too much given that I want unit-test.arc to work with arc3.1.
To play devil's advocate (and/or nihilist), why do you need that? Error messages in Arc are already universally this way, so it's expected behavior that the messages look crappy. :P
On a more constructive note, is there some part of your code (or could there be, with some rewriting) where you could specifically catch the exception whose message you want to display nicely? Seems like you could probably work out some way to wrap something like:
arc> (on-err (fn (c)
(pr "Error: ")
(prn (details c)))
(fn () (err "a \"b\" c")))
Error: a "b" c
It's a bit of hack, but if you need to subvert the language's defaults in an implementation-conforming way, them's the breaks.
Side note: paging through your source code, I notice you have a few functions to support to-readable-string. I believe it could be greatly simplified, because write's job is already to print out values in ways that read can parse back in. Thus, save for the single-quote stuff, I think you could boil it all down to tostring:write. Play around with it and see if it's what you want:
arc> (tostring:write (list 'a "b" (obj c 'd)))
"(a \"b\" #hash((c . d)))"
> To play devil's advocate (and/or nihilist), why do you need that? Error messages in Arc are already universally this way, so it's expected behavior that the messages look crappy. :P
Because I'm not satisfied with crappy behavior. It makes readability worse, and debugging harder.
I'll take a look to see if I can catch it better; reading over my code, some of it does seem to be convoluted, if working. I've never quite been happy with how the failure messages are calculated; if that changes, I can easily change how we handle printing the errors.
Other than the double-quote issue, I don't recall other big reasons to use to-readable-string. I'd certainly be a fan of removing code, if I can make it work. It would bring my macro:function ratio below 1:1 again, which would be awesome. :)
The reason to use 'write rather than 'display is precisely so that things can be pasted into the repl. All you should have to do is 'read and 'eval the entire error string.
But there's definitely something busted about this. In anarki:
arc> (= x "abc")
arc> (err x)
Error: "abc" ; ok, looks good
arc> (= x 'abc)
arc> (err x)
Error: "error: abc" ; whaa..
There's also a long-standing issue that's bothered me before:
arc> (= x "abc")
arc> (err x " shouldn't be a string")
Error: "abc \" shouldn't be a string\""
You're right, that's pretty ghastly. It should print:
Error: "abc" shouldn't be a string
or:
Error: "\"abc\" shouldn't be a string"
I'm still mulling what the culprit is here, but I don't think it's write vs display.
The culprit is that 'err is defined to be Racket's 'error. It looks like every single use case of 'error is discouraged for one reason or another in the Racket reference:
- (error sym) creates a message string by concatenating "error: " with the string form of sym. Use this form sparingly.
- (error msg v ...) creates a message string by concatenating msg with string versions of the vs (as produced by the current error value conversion handler; see error-value->string-handler). A space is inserted before each v. Use this form sparingly, because it does not conform well to Racket’s error message conventions; consider raise-arguments-error, instead.
- (error src frmat v ...) creates a message string equivalent to the string created by
(format (string-append "~s: " frmat) src v ...)
When possible, use functions such as raise-argument-error, instead, which construct messages that follow Racket’s error message conventions.
Er, I knew it was weird for me to say "'err is defined to be Racket's 'error," but I just realized, that factoid was in the original post of this thread. :-p