For the connective narrative, how about using something like the existing [[ref]] markers that were in the docstrings to provide 'tags' for each entry? Every entry would then be associated with a set of tags that includes it's own name + any tag mentioned in its docstring.
This gets you two things.
1) You can now provide grouped details pages, like the 'list operations' and
'macros' pages from the arc3.1 docs.
2) You can provide connective narrative associated with a particular tag, in a separate document.
It probably gets you other things too. Maybe we could add the ability on the command line to filter help by tag instead of just name. That would be nice.
Cool idea! I used to do something a long time ago: use a little-known option of ctags (-r) to match wikiWords, so that hitting C-] in vim or M-. in emacs with the cursor on say 'databaseSubsystem' would take you to '$<databaseSubsystem>' in the codebase.
The nice thing about it is that a name could potentially belong to multiple different tags/narratives. Multiple overlapping trails is a pretty decent way to narrate an intrinsically non-linear thing like a codebase. See also http://leoeditor.com.
I like the examples stuff. The only issue that I have with it is that you are providing explicit, yet non-tested results. It makes sense to me to either provide just the code and have the documentation system generate the results, or to provide test cases instead.
Ideally, the 'results' in the documentation would still be computed dynamically, so you can always see whatever the current output would be, but also have the expected results available for separate regression testing.
Also, I think it would be great if the regression test results could be automatically generated and stored somewhere else, because I don't want to have to manually type in expected results for every case. Some mechanism to 'freeze' the current output as the accepted value for regression purposes would be cool.
Yeah, that whole side is a bit clunky. It gives readers feedback when things are broken so they aren't misled. But the job of the writer is still hard. Things would be improved by my last idea, to run examples along with other tests.
Here's a couple more issues:
a) Expected results are currently unevaluated, so there's no way to represent tables, and so on.
arc> (help counts)
[fn] (counts seq)
Returns a table with counts of each unique element in 'seq'.
Examples:
arc> (counts '(b a n a n a))
(obj b 1 a 3 n 2) <-- ERROR
b) We can't provide examples for non-deterministic functions like rand-elt.
c) We can't show stdout and stderr.
---
Last but not least, I've been thinking about the freezing idea myself in other contexts. I think there's something big there.
As for the 'freezing', I've been thinking about it in other contexts as well. I noticed that one of the companies I worked at seemed to want to do some of their regression tests that way, so it makes sense to provide a way to automate it.
In particular, I would like a way to convert an interactive console session (my normal method of testing code) into a set of unit tests. Not sure exactly what the right way to do that would be though.
It's still not a very good form of unit test, as it won't cover cases as mentioned above with randomness or side-effects. Mutation testing or fuzz testing / generators are probably better ways to generate unit tests, but they don't make very good examples.
Yeah. B can probably work just fine with dynamically evaluated examples, but stderr/stdout would require user interaction, which may not be acceptable. Maybe some way to indicate if an example should be evaluated or not?
What do you mean by 'run examples along with other tests'? Does that mean that you would include other tests in the help output? That the examples would be included when running the tests? Or something else?
(examples do
(do (prn "line 1")
(prn "line 2")
(prn "line 3"))
_)
Here's how it looks:
arc> (help do)
[mac] (do . args)
Evaluates each expression in sequence and returns the result of the
last expression.
Examples:
arc> (do (prn "line 1")
(prn "line 2")
(prn "line 3"))
It makes sense to specify the example expr / expected pairs as sexps & strings: run repl-output on exprs, compare those strings to the expected strings.
Thanks for thinking through that! Yes, I'd had a similar though not as fully-formed idea, but was stuck on distinguishing stdout from return value in the examples. But yeah, that's overthinking it -- we have to mentally parse the two at the repl anyway.
Only other quibble: a string isn't very nice to read next to code, even if it looks nice at the repl. Hmm..
Maybe we should make an auto scraper/notifier? Though iirc, the arc server doesn't really appreciate frequent queries. On closer look, it should be fine; the default throttle window should be 10s. It could query once a minute, and update an rss feed for arc forum posts and comments. Or provide webhook + email support.
I actually built one back in the day: http://akkartik.name/post/2011-05-13-05-47-29-soc. Trouble was, it kept getting banned/blocked even when I scaled back to crawl HN every five minutes. Eventually I was crawling so infrequently that I was missing stories that dropped off /newest in the meantime, so I stopped crawling.
I actually had an arc version for a brief period[1], but it didn't seem like there was as much need for it here. But perhaps the email notifications would be useful. I'll see if I can bring it back.
[1] I was certain I'd showed it here, but no I only showed it over email to a couple of folks. I really should bring it back just so y'all can look at it. Or at least locate the (rails) sources.
Wart isn't that different from arc, it still shows its roots.
for x 1 (x < 10) ++x
prn x
map prn '(1 2 3 4 5 6 7 8 9 10)
def (f n)
prn n
if (n < 10)
(f n+1)
f.1
Unlike smile it needs those parens around the if condition.
Edit 13 hours later: Ack, I was wrong. Wart would drop the parens just fine!
if n < 10
(f n+1)
I'm not sure I like it, but the design of infix fundamentally supports it by having higher precedence than function calls. If the infix operands grow complex it can get hard to read without parens.
if (function-call n x1 x2 x3) < 10
(f n+1)
# still works, but yuck..
Rather than compare it to arc, I found it tantalizing to think about how I'd implement that syntax. It looks like he's built objects for modules/namespaces, ranges, etc. But he's somehow supporting infix without spaces and also names like print-line. How does it work?! :)
Edit 8 minutes later: Also, the square brackets around the call to [f 1] are strange. If they're mandatory, there's something there I don't follow.
Interesting direction to take it. It would be interesting to know how he implemented it, so that he can still consider it to be a lisp. In particular that '#include' syntax sticks out a bit.
I also found it ironic how conflicted he is over its relationship with lisp.
There might be more, but these should get you started.
I should probably mention that most of that documentation is probably targeted at the the vanilla arc 3.1, which is the lowest common denominator at this point for all of the arc implementations. Every other arc implementation has its own set of additional libraries, and usually a way to access libraries from its host language. I.e., anarki also includes multiple ways to call racket code directly.
I wonder what will happen to Arc now that pg has passed YC on to Sam? How is he going to pick up work again? Will he just keep working on arc 3.1, or will he just contribute to anarki?
If pg ever contributes a line of code to anarki I'll eat my hat :) Not that that's a bad thing; it works perfectly well for each side to port changes from the other.
I think it might help to point out how the macro expands there:
(inl 1 '(1 2 3))
is transformed by the reader to
(inl 1 (quote (1 2 3)))
which is then macro expanded by
`(in ,elt ,@lst)
where elt is bound to '1, and lst is bound to '(quote (1 2 3)). This means that when lst is spliced in, you get:
'(in 1 quote (1 2 3))
As stated, the problem is largely the choice of macro instead of function. Macros are primarily useful to control evaluation; they do not evaluate their arguments by default. A macro will only let you work with the literal input, not their values. You can't (with arc) have your cake and eat it too; you can only work with either the syntax, or the values. You wouldn't be able to do something like:
(let x '(1 2 3)
(inl 1 x))
for instance, because it would attempt to treat the symbol 'x as if it were a list to be spliced, not the value of x. That's why you need a function, like pos.
Not so. He restarted the wat project, with the goal this time being a super lightweight vm on which to build more useful languages. For now, the code is only ~350 lines of javascript and there's no parser or anything, it just uses json lists of strings. With the core wat-in-wat pieces, it comes up to just over 500 lines.