No offense, but I'd like to give your comment an awful lot of errata:
Those objects you're creating will have the cons list (quote a) as a key. Non-atomic keys are full of gotchas because Arc's tables use use Racket's 'equal? for key comparison, rather than using 'is or 'iso. This means tables care whether a list ends in the symbol 'nil or the Racket empty list (), which is a detail that's otherwise invisible in Arc.
(If you want the symbol 'a as a key, the code is (obj a 'b).)
Arc's tagged types are not lists, and 'iso can support lists without supporting tagged types. (Personally, I wouldn't want 'iso to dig around in my tagged types anyway. I like pretending the data inside is encapsulated, except where my code absolutely needs to access it.)
Finally, does 'iso actually support tables in Anarki? I can't try it out right now, but I don't see any code for this when I search GitHub.
(defextend iso (x y) (isa x 'table)
(and (isa x 'table)
(isa y 'table)
(is (len keys.x) (len keys.y))
(all
(fn ((k v))
(iso y.k v))
tablist.x)))
; default impl for tagged types
(defextend iso (a b) ($.vector? a)
(iso ($.vector->list a)
($.vector->list b)))
Does the 'default impl' trigger your gag reflex? :)
Oops, I assumed Github's search would show me every occurrence in every file, like a recursive grep. No such luck, I guess. :-p So you got me.
While that's a reasonably useful default behavior for 'iso on tagged types, I'm not particularly happy to see it.
Arc provides very few helper utilities for working with tagged types--basically just the axioms 'annotate, 'type, and 'rep, plus 'isa and 'coerce--and the documented purpose of tagged types is for user-defined types. I came to Arc from a more OO-like background and saw this as a good way to control access to the implementation details of my types. Unless you explicitly called 'rep, you didn't get more coupling than you bargained for. This gave 'rep almost an "unsafe" reputation for me.
If 'iso gets to access the rep at will, then I may have been programming in Arc less robustly than I expected: Either each of my types should have come with an 'iso extension to reinforce my encapsulation, or I should have considered 'iso "unsafe" and used it less often. Not that I could have known this at the time! :)
But it's not a big deal. I do deep comparison operations sparingly enough as it is, and I don't know if my soft encapsulation technique actually matters because I rarely saw anyone else following similar guidelines. (I've rarely seen anyone using 'annotate at all, actually, and I think it's been a common complaint that 'annotate is basically 'cons with fewer utilities built up around it. That's a plus for me, I guess, lol.)
A good example of me considering an operation "unsafe" is earlier in this thread, where I discouraged the use of compound values as table keys. It just so happens Arc's table key comparison breaks the encapsulation of tagged types as well. :)
No danger of it happening here. Arc forum is running some ancient version from what I can tell. They probably learned to stop messing with it after the voting ring detector went haywire. I wonder how many of us remember that :)
The error "Invalid cross-device link" happens if you're trying to move a file across partitions. Apparently sometimes people have set up their home directory in a separate partition, even without realizing it.[1] So I'm guessing you're in a situation like that.
Other languages seem to recover from this particular error and do a copy and unlink instead:
Racket's reference for 'rename-file-or-directory states that it can move files to other paths "on the same disk"[2], and it goes into detail about atomicity guarantees, so this behavior is probably by design.
So the way you can find resolution here might be:
- Somehow change what temp directory Arc uses.
- Put the Arc directory elsewhere on your filesystem.
- Somehow change your partitions so the home directory and temp directory are in the same one.
- We should probably update Arc to recover from this somehow. :)
I think all the errors you're seeing are due to running out of heap. How much memory do you have? Is there some per-process limit, perhaps to RLIMIT_AS?
Edit 35 minutes later. I can reproduce that it dies for me at the precise same iteration as you. And RLIMIT_AS is infinity, so that's not the issue. I've pinged the author for comment.
Thank you. But now I think it would be wise not to complain about the issue on the forum, but rather to create an issue on the project's github. Will do it now.
Well, thanks for the bug report. Well, I'll look into it when I have the time: the work which pays my bills has caught up with me once again and I don't have a lot of time to do hobby development. :)
The assertion error is a stack overflow. :) At present Arcueid makes use of a fixed-size stack within its virtual machine, and since the compiler still can't properly optimise tail recursion, your code overflows the stack.
That was what I thought :) Is there a place where I can increase the size of the fixed stack? I looked for the limit in the code, but couldn't find it.
Does it make sense to resize it on the fly when we discover we've overflowed the stack? Or are there potentially continuation pointers into the stack that would be non-trivial to track down?
Well, I haven't tried to run crawler with large depth setting yet. I discovered, that it have strange problems with threading and almost unable to process in parallel. Then I tried to investigate why. Will make a separate post on it, when will get some valuable information.
In case it helps (or just drives a discussion), I've ironed out my own design spec for the features you're going for.
It's not quite the same as what you're describing. For one thing, it doesn't include the new restrictions you're talking about.
For another thing, it includes a new pattern type. Symbols have a lot of duties in your patterns because they deal with evaluation and keywords. I found it easiest to separate symbol patterns into two different types of patterns, only one of which deals with keywords. The non-keyword patterns I represent as strings (just to distinguish them from symbols). It would be easy to replace this with a special form or with some other literal type.
...Oh, I guess you don't have an explicit keyword parameter syntax either. I assumed you did, so it's in here too. (It could actually help clarify what's going on here. The symbol pattern deals with keywords and does just a bit more on top of that, so I like the side-by-side comparison.)
---
The pattern language has several kinds of patterns. A pattern operates on a single unevaluated s-expression (henceforth "expr") and an evaluation environment, and it outputs a map of variable bindings. It also outputs an evaluation result, if it evaluates the expr as part of its processing. (The evaluation result is only used for (<a> | <b>) patterns, to prevent duplicate evaluations.)
These are the available patterns:
<str> ; where <str> is a string
Evals the current expr and binds the variable named <s> to the result value.
<s> ; where <s> is a symbol
Runs the pattern <str> on the current expr, where <str> is the string name of the symbol <s>.
Uses those variable bindings and that evaluation result.
(That may look simple enough, but a completely different symbol pattern applies when matching a list element. See below.)
'<a>
Runs the pattern <a> on a quoted version of the current expr.
Uses all variable bindings produced by <a>.
()
Evals the current expr. If the result isn't nil, raises an error.
; any cons cell
Does several special things depending on the contents of the pattern's cons cell:
(<a> | <b>)
Runs pattern <a> on the current expr.
If that pattern returned an evaluation result, runs pattern <b> on a quoted version of that result value. Otherwise, runs pattern <b> on the current expr.
Uses all variable bindings produced by <a> and <b>, preferring the bindings in <b>.
Uses the evaluation result of <a> or <b> if available, preferring the result of <b>.
(<before...> <k> <v> ... <rest>)
; where <before...> is any number of non-keyword, non-symbol
; elements; <k> is a keyword; and <rest> could be anything,
; especially an improper list
This pattern treats the current expr as an unevaluated improper list, looking for the first occurrence of <k> with another element after it (which we will refer to as the value expr).
If the keyword binding is found: Matches <v> against the value expr. Matches <rest> against an alteration of the current expr, changed to remove the keyword and the value expr. Uses all variable bindings produced by <v> and <rest>, preferring the bindings in <rest>.
If the keyword binding is not found: Matches the pattern <rest> against the entire expr. Uses those variable bindings.
(<before...> <s> ... <rest>)
; where <before...> is any number of non-keyword, non-symbol
; elements; <s> is a symbol; and <rest> could be anything,
; especially an improper list
Let <k> be the keyword with the same name as the symbol <s>, and let <str> be this name as a string.
This pattern treats the current expr as an unevaluated improper list, looking for the first occurrence of <k> with another element after it (which we will refer to as the value expr).
If the keyword binding is found: Matches <str> against the value expr. Matches <rest> against an alteration of the current expr, changed to remove the keyword and the value expr. Uses all variable bindings produced by <str> and <rest>, preferring the bindings in <rest>.
If the keyword binding is not found: Matches the pattern (<str> ... <rest>) against the entire expr. Uses those variable bindings.
; for any other kind of cons cell...
(<a> ... <b>)
Evals the current expr. If the result isn't a cons cell, raises an error. Otherwise, runs the patterns <a> and <b> on quoted versions of the result's car and cdr.
Uses all variable bindings produced by <a> and <b>, preferring the bindings in <b>.
I found another mistake I made. In the two rules that use <before...> and <rest>, that <before...> should be used whenever the rule matches the remainder of the expr.
In particular, for symbols to work as positional args whenever there isn't a keyword, the symbol rule needs to fall back to (<before...> <str> ... <rest>), reflecting the original location of the symbol in the pattern. I just had it falling back to (<str> ... <rest>) for some reason.
---
(<before...> <k> <v> ... <rest>)
; where <before...> is any number of non-keyword, non-symbol
; elements; <k> is a keyword; and <rest> could be anything,
; especially an improper list
This pattern treats the current expr as an unevaluated improper list, looking for the first occurrence of <k> with another element after it (which we will refer to as the value expr).
If the keyword binding is found: Matches <v> against the value expr. Matches <rest> against an alteration of the current expr, changed to remove the keyword and the value expr. Uses all variable bindings produced by <v> and <rest>, preferring the bindings in <rest>.
If the keyword binding is not found: Matches the pattern <rest> against the entire expr. Uses those variable bindings.
(<before...> <s> ... <rest>)
; where <before...> is any number of non-keyword, non-symbol
; elements; <s> is a symbol; and <rest> could be anything,
; especially an improper list
Let <k> be the keyword with the same name as the symbol <s>, and let <str> be this name as a string.
This pattern treats the current expr as an unevaluated improper list, looking for the first occurrence of <k> with another element after it (which we will refer to as the value expr).
If the keyword binding is found: Matches <str> against the value expr. Matches <rest> against an alteration of the current expr, changed to remove the keyword and the value expr. Uses all variable bindings produced by <str> and <rest>, preferring the bindings in <rest>.
If the keyword binding is not found: Matches the pattern (<str> ... <rest>) against the entire expr. Uses those variable bindings.
---
After this fix:
---
(<before...> <k> <v> ... <rest>)
; where <before...> is any number of non-keyword, non-symbol
; elements; <k> is a keyword; and <rest> could be anything,
; especially an improper list
This pattern treats the current expr as an unevaluated improper list, looking for the first occurrence of <k> with another element after it (which we will refer to as the value expr).
If the keyword binding is found: Matches <v> against the value expr. Matches the pattern (<before...> ... <rest>) against an alteration of the current expr, changed to remove the keyword and the value expr. Uses all variable bindings produced by <v> and (<before...> ... <rest>), preferring the bindings in the latter.
If the keyword binding is not found: Matches the pattern (<before...> ... <rest>) against the entire expr. Uses those variable bindings.
(<before...> <s> ... <rest>)
; where <before...> is any number of non-keyword, non-symbol
; elements; <s> is a symbol; and <rest> could be anything,
; especially an improper list
Let <k> be the keyword with the same name as the symbol <s>, and let <str> be this name as a string.
This pattern treats the current expr as an unevaluated improper list, looking for the first occurrence of <k> with another element after it (which we will refer to as the value expr).
If the keyword binding is found: Matches <str> against the value expr. Matches the pattern (<before...> ... <rest>) against an alteration of the current expr, changed to remove the keyword and the value expr. Uses all variable bindings produced by <str> and (<before...> ... <rest>), preferring the bindings in the latter.
If the keyword binding is not found: Matches the pattern (<before...> <str> ... <rest>) against the entire expr. Uses those variable bindings.
"The next one refers to s and str as well. Is that intended?"
Yes. Only <s> appears in the pattern itself, but the meaning of <str> is explained by "...where <str> is the string name of the symbol <s>."
---
"Also, I don't follow the distinction between the string case and the symbol case."
Did you see this part? -- "(That may look simple enough, but a completely different symbol pattern applies when matching a list element. See below.)"
A symbol which occurs in a list acts as a hybrid keyword/positional argument. A symbol by itself acts just like a string.
---
Sorry I'm being terse with this stuff. I found it hard to express my points in informal English, which is why I went with a spec document style in the first place.
Let's see...
My motive is to show you a way your desired features can be accomplished in an extensible ravioli style, rather than one big chunk of spaghetti. What you want is achievable with a simple enough collection of parser combinators, although sometimes their interfaces may be unusual due to expression evaluation being part of the parsing process.
For instance, in the system I gave, the parser combinators use a few unusual design patterns:
- The combinators must take in an evaluation environment.
- The combinators frequently quote a value so they can pass it off to another combinator that may eval it again. (I think I've seen you use this style before, actually.)
- The combinators must sometimes return the evaluated expression, just so that the (<a> | <b>) pattern can avoid double evaluation.
Another important point I'm making is that you probably don't need to reorder the argument list for the purposes of keyword arguments. My system instead (effectively) reorders the parameter pattern so the keywords are processed first.
I got this system a bit wrong because I don't support multiple keyword aliases for the same parameter. My (<a> | <b>) pattern instead binds multiple parameter variables to the same argument value, so I got that backwards.
This seems really useful. I got the motivation off the bat, but I still don't understand the spec. Can you give an example session showing how functions are defined, and how they are called?
'(a b) ; the parameter list
(a b) ; the args to (list ...)
((+ 1 1) (+ 2 2)) ; the args to (foo ...)
((+ 1 1) (+ 2 2)) ; the result
(baz)
(baz)
((+ 1 1))
; Error! Can't make the function call (2).
'(a b c)
(a b c)
((+ 1 1) :c (+ 3 3) (+ 2 2))
((+ 1 1) (+ 2 2) (+ 3 3))
'(a :b b c)
(a b c)
((+ 1 1) (+ 2 2))
; Error! Unbound variable b in expression (list a b c).
So I have some more kinks to work out, lol. I finally appreciate your predicament. :)
The (baz) issue is caused by the complexity of the fact that in a normal function call, each of the arguments should be evaluated, but the list of arguments shouldn't. So if the traditional Arc parameter list (baz) is a pattern, it should destructure an unevaluated list in such a way that the baz pattern inside operates on an evaluated version of the list's first element.
It would be a lot simpler if we wrote this argument list differently:
> (def foo (baz) ...) ; doesn't work
> (def foo `(,baz) ...)
> (def foo (arg-list baz) ...)
; where (arg-list ...) is a pattern special form
I think that last one is promising, because then we can just write (baz) and the macroexpansion of (def ...) can insert that into (arg-list baz) for us. Then we get to have slight differences between how parameter lists work and how list destructuring works.
As for the "unbound variable b" issue, I think that could be solved by having each pattern take an optional current expr, rather than a mandatory one. My exact implementation of this would probably vary substantially based on what else I was trying to do. I might want a guarantee of which variables will be bound by a pattern long before there's an actual argument list to match it against, for compilation purposes and such.
In these examples, the first error happens during macroexpansion, when the compiler is processing the 'fn special form. The second error happens at run time. They both recover to the REPL just fine.
The second stack trace doesn't include 'foo or 'map, but I bet that's because they made a call in tail position, leaving no trace of their stack frame. Let's change 'foo so it doesn't do that:
Arc seems to throw away source location information, but I think we could get that back too if we tried. It'll involve using Racket syntax objects somehow.
Thanks for speaking up about this. This seems like something that could help a lot of people. XD
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.
"This idea has something in common with IDEs that highlight all occurrences of the word at the cursor."
This is a feature I miss when I can't have it. I bet this highlighting would really help when reading printed-out code or code examples on a blog.
---
Your approach isn't semantic, in the sense that it doesn't discriminate between similarly named variables based on the language's actual scoping rules, but I wouldn't worry about that too much. When I name two variables the same thing, they usually are related in some way. Sometimes they all hold the same value most of the time, or sometimes they all have the same syntactic significance (e.g. CPS callbacks), or if all else fails... they have the same name. ^_- If it annoys me that they're highlighted the same way, it probably also annoys me that they have the same name.
---
You may also be interested to know that Nulan (well, the last stable version of Nulan) highlights variables differently depending on whether they're macros, locals, or non-macro globals.
Yeah I've run into this already. In the screenshot for my original solution I highlighted a variable called test. Oops, poor name. Trouble is that this change now causes poor names in tightly localized contexts to bleed through the entire codebase :/
There's two directions to solve this:
a) Automatically detect variables with long lifetimes (measured in lines) to highlight. Perhaps this is a refinement of highlighting for
*globals*
b) A way to attach metadata on a per-function or per-fragment basis. Highlight test in this function but not elsewhere, etc.
In particular, ports->ssl-ports looks like it might be a big help here. In fact, it has an option for TLS, so I guess this is the right place to look. :)
Since 'dynamic-send is provided by the racket/class module, doing 'xdef with libraries should work.
Some notes:
If ac.scm used the racket module instead of the mzscheme module, it would have access to all the racket/class bindings to begin with, and then I suppose I'd use a different example. ^_^
In Arc 3.1, the GUI that appears is unresponsive for me. If I Ctrl-C once in the REPL, the REPL stops processing input but the GUI becomes responsive. If I do it again, the GUI closes. I fumbled with threads trying to make the REPL and the GUI work concurrently, but nothing worked, and I've given up for now.
In Anarki, the GUI that appears is unresponsive, but doing Ctrl-C in the REPL exits Racket entirely, including the GUI. Again, my attempts to use threads haven't helped.