You got me rereading R-1RK, and I was reminded again of how much I like the way Shutt connects up his design decisions to design goals and constraints. It's very in the spirit of Christopher Alexander (http://www.amazon.com/Notes-Synthesis-Form-Harvard-Paperback...).
In spite of the level of detail, kernel's design goals diverge so quickly from my own that I can't reuse any of the design work. It's really too bad.
Today's frustrating example: I really couldn't give a rat's ass that Kernel permits cyclical argument lists in operatives but not applicatives. Then in A.4 he talks about what's missing before R0RK, and the support for cycles feels pedantic next to the problem of a fast implementation. If he wasn't so concerned about handling cycles during eval perhaps the code wouldn't be slow. How the heck can a language be slower than wart?!
:) I assumed you were saying that wart wouldn't be able to include '+' in symbol names, but that was already true before this change. Wart has other constraints, but if you can use '&' for something you can also use '+'.
Does that trigger memories? Did I understand you right? Were you nostalgic for the good old days when wart symbols could include '+'? ^_^
My solution to this (while breaking compatibility with Arc) is to just not have mutable cons cells. I only see two legitimate uses for mutable cons cells:
1) Speed. But honestly, if you're micro-optimizing your Arc code by mutating conses...
2) Intentionally infinite lists, like akkartik's "nils":
(= nils (list nil))
(= cdr.nils nils)
I'm not concerned about the first case, especially because a sufficiently smart compiler should be able to handle immutable cons cells reasonably (I believe Racket does).
As for the second case... I think that's solved by adding in a more general system for dynamically generating lazy lists. Then akkartik's "nils" can be written like this:
Basically, lazy-cons takes two thunks: the first is called to extract the car, and the second to extract the cdr. This lets you dynamically create all kinds of nice things, like streams, and it isn't limited to constant values like cyclic lists are.
Thus, you get all the benefits of immutable conses and a strictly more powerful system for lazy list generation. The only possible downside is some performance, which I don't think would be a big deal.
Of course, I already know you're going for full Arc compatibility, so this post is probably useless, but oh well.
I'd assumed I could apply Floyd's hare to handle cars as well, but you're right, now I'm not so sure. The key difficulty is that there's two dimensions, so does the number of hares double each iteration?
Here's a flat-cycle-detecting iso I've been playing with:
def (iso a b)
((afn ((a harea|through) (b hareb|through))
(if ~list?.a
(a = b)
(addr.a = addr.harea)
(addr.b = addr.hareb)
:else
(and (iso car.a car.b) # Can't detect cycles through car.
(self `(,cdr.a :through ,cdr+cdr.harea)
`(,cdr.b :through ,cdr+cdr.hareb)))))
(list a :through cdr.a)
(list b :through cdr.b))
I don't know what my point was in posting that link--I mulled over it for a long time before just posting what I'd typed in--and now I don't understand your reply. What did you take off the table, and when? ^_^;
Both of the things you're talking about look specialized for cycles in flat lists. I think dido's examples could have just as easily used 'scar instead of 'scdr.
arc> (= x '(1 2))
arc> (scar x x)
((...) . 2)
#0=(#0# . 2)
I do like the (1 2 ... (3 4 ...)) notation for cyclic lists though. :)
The Kernel R-1RK has a good approach to treating flat cyclic lists as though they're a usual case. IMO, it means everything that deals with lists is more complicated to explain and arguably doesn't even deal with "lists."
1. Using a hash table for cycle detection can get prohibitive for large cycles. Fortunately there's Floyd's algorithm which detects cycles without the space overhead: https://en.wikipedia.org/wiki/Cycle_detection#Tortoise_and_h.... Here's how you might print an infinite list, one item to a line:
-- z.wart
def (print x hare)
if ~list?.x
prn x
(addr.x = addr.hare) # like arc's is
prn '...
(do (prn car.x)
(print cdr.x (cdr+cdr (or hare x)))) # + is compose
# create a cycle
(<- x '(1 2)
lastcdr.x x)
prn (firstn 5 x)
print x
To run it:
$ git clone http://github.com/akkartik/wart
$ cd wart
$ git checkout f171a9d878 # Unnecessary except for visitors in the far future
$ ./wart
ready! type in an expression, then hit enter twice. ctrl-d exits.
load "z.wart"
(1 2 1 2 1)
1
2
...
2. You mentioned a couple of possible notations for cycles in lists. Here's my suggestion:
(1 2 ...) # means (1 2 1 2 ..)
This works especially well because wart uses ... for dotted lists:
(1 ... 2) # read as '1 consed with 2'
(1 2 ...) # read as '1 2 consed with itself'
Finally, you could represent more complex cycles as well:
Here's a discussion I remember every time I think of whether or not to use + as an identifier character: http://arclanguage.org/item?id=10069 "Looks like the answer is to use & instead of +. I like the look of + better, but I can tell people are going to want to use it in names."
Yeah, I don't actually want to see rationals at the prompt. It's just that lisp has this nice distinction between 'write and 'print, with the guarantee that 'read can handle anything 'write emits. I'm loath to have write+read change a value in subtle ways.
In this case, that the repl uses 'write is an unfortunate constraint. Still thinking..
Well, the whole reason I did it in Arc/Nu was because I liked to use the REPL as a calculator, and I hated having it print rationals. So, having the REPL print rationals kinda defeats the point in my eyes, but I don't care since I don't use Anarki.
Thanks, I ended up going with the third option, but with a twist: display prints inexact but write prints exact. So at the prompt you still see exact numbers:
Since that's being done in Anarki, I'd rather use $.exact->inexact, rather than relying upon Racket's auto-coercion. My trick is best used in Arc 3.1 which doesn't have (easy) access to Racket.
"Should exact or inexact be the default subtype for num?"
I see at least a few good solutions:
1) Use floating point everywhere. This is what JavaScript (and Nulan) do. Simpler, but it means you give up exact math on rationals.
2) Use floating point by default but support coercing to exact. This may or may not lose information, I don't know. Alternatively, support exact by default and coerce to floating point.
3) Do things the way Racket does it, but when printing the number, coerce it to floating point. This is what Arc/Nu does:
> 3/2
1.5
This has the benefit that math calculations are exact, but the result is printed in a more readable fashion (more readable to me anyways).
I just want to add a link on the top bar, like a "news" tab, then the visitor click on that it will turn to http://blog.geek.vn subdomain. Anyways, the previous threat submitted by jwk is so helpful to me.