I can't help but feel this is the wrong way to approach the problem. Even if you got your current definition working, there are too many ways for it to break. e.g.,
(isdef (table [for i 1 3 (= _.i (* i i))]))
reads in as
(isdef (table (fn (_) (for i 1 3 (= _.i (* i i))))))
and fails. There's the special form fn (neither lexically nor globally bound), plus the tricky business of checking defined when compiling fn so that _ is actually lexically bound (otherwise, it appears to be undefined). Same goes for expanding the for macro and i, I imagine.
All this parsing / partial compilation smacks of over-engineering. Even if you avoid multiple-evaluation issues, it seems too easy to make a typo in the middle of a contextual each like
(myeach (table [for i 1 3 (= _.i (* i u))])
(prn _))
where u is undefined, meaning it tries to destructure the table expression against (prn _), leading to confusing errors:
arc> (each (table [for i 1 3 (= _.i (* i u))]) (prn _))
Error: "Can't understand fn arg list 1"
The syntax for this each you're trying to write is suspect. From a certain angle, it almost seems like trying to overload on arity even though both forms take an unlimited number of values (on account of the rest parameters).
For all I know about Perl (which isn't much), it looks like its foreach works with explicit and implicit variables because you can consistently parse it -- you always have parentheses delimiting the array.
foreach $item (@items) { print $item; }
vs
foreach (@items) { print $_; }
If that's the case, the equivalent doesn't map cleanly over to Arc, since the syntax for
(each item items (prn item))
looks effectively the same as
(each items (prn _))
to the parser.
I know it's probably not what you want, but perhaps you could consider something with a more regular syntax?
(mac myeach ((var expr) . body)
(when (no expr)
(= expr var var '_))
`(each ,var ,expr ,@body))
arc> (myeach ('(1 2 3)) (prn _))
1
2
3
nil
arc> (myeach (v '(1 2 3)) (prn v))
1
2
3
nil
arc> (myeach ((k v) '((a 1) (b 2) (c 3))) (prn k ": " v))
a: 1
b: 2
c: 3
nil
arc> (myeach ((k v) (table [for i 1 3 (= _.i (* i i))])) (prn k ": " v))
2: 4
1: 1
3: 9
#hash((3 . 9) (1 . 1) (2 . 4))
arc> (myeach ((table [for i 1 3 (= _.i (* i i))])) (prn _))
(2 4)
(1 1)
(3 9)
#hash((3 . 9) (1 . 1) (2 . 4))
arc> (macex1 '(myeach (a b) c))
(each a b c)
arc> (macex1 '(myeach (a) b c))
(each _ a b c)
arc> (let v 2 (myeach (v '(1 2 3)) (prn v)))
1
2
3
nil
arc> (let lst '(1 2 3) (myeach (lst) (prn _)))
1
2
3
nil
The extra parentheses are kind of ugly when you have expressions (e.g., a call to table), but I find that I'm most often iterating over variables anyways. Or you could rely on the Scheme reader recognizing curly-braces as parens:
> The syntax for this each you're trying to write is suspect
> perhaps you could consider something with a more regular syntax?
You're right. The more I think about it, the less I like this 'each idea.
> All this parsing / partial compilation smacks of over-engineering.
True, but this is also part of the fun here ;-)
> For all I know about Perl (which isn't much), it looks like its foreach works with explicit and implicit variables because you can consistently parse it -- you always have parentheses delimiting the array.
You are absolutely right here, and you made me realize I am actually trying to get something more dirty/complicated than in Perl, which is... really not a good sign :-D!
> Or you could rely on the Scheme reader recognizing curly-braces as parens
This is also an interesting option. Good to know.
> Sorry I don't have any better suggestions.
Gosh, that's already a bunch of good ideas! Thanks!
> Which is what makes a macro like this so hairy: the inconsistency.
Yes.
> There are the lexical bindings you want to use as iteration variables [...] and the ones you want to iterate over
Yes "context" stuff. See Perl.
> Maybe what you want to do is specialize on whether the second parameter evaluates to an iterable object?
Yes, it may be a way.
> don't know if I'd like seeing this in use -- complexity creep.
You are certainly wise here :-).
But you know, I don't care of 'each. It's OK if in current Arc it is too tricky to have, and I don't try anymore to have it work my way. It will anyway always be tricky and whether something like this should be used is another debate.
Actually what I care more about is the 'if behaviour. Everyone here seems to ignore this eventual issue and focus on the precise problem of 'each, but this was not my point initially.
ignore this eventual issue and focus on the precise problem of 'each
Certainly. It was in the back of my head when I wrote my reply. I didn't mention it because I was trying to be terse (I have a problem being long-winded), so opted to focus on something small in my post.
But I agree: macroexpansion time is kind of fuzzy in Arc. It even gets in the way of things like
arc> (let do [+ _ 1] (do 1 2 3))
3
which has been a source of hand-waving for awhile with no clear answer: http://arclanguage.org/item?id=9696. But that's another story (in a manner of speaking).
Not to pick on the intended each behavior (after all, why bother writing code if you don't like how it looks?) or the actual topic of this post.
Thanks for the link and (let do ...) example. Interesting to read.
> macroexpansion time is kind of fuzzy in Arc
Well, this was the point of this post! It is important to know about the "limits" of your macros system. As CatDancer mentioned it, there are differences between them.
The catch phrase "macros are evaluated at expansion time, functions at runtime, ..." is too simplistic and doesn't provide enough knowledge at some point.
> why bother writing code if you don't like how it looks?
I'm guessing v372 for Ubuntu probably works on Debian, since they're largely similar. If it doesn't, I've been running v360 on squeeze and haven't noticed any issues with arc3. Failing either of these, you could build v372 yourself.
Once MzScheme is installed, there's not much to do about Arc that's not already on http://www.arclanguage.org/install. You might consider aliasing it in your .bashrc (or whatever), and I recommend using rlwrap. e.g.,
alias arc='cd ~/arc3; rlwrap mzscheme -m -f as.scm'
I would add (for other potential readers) that it is a very neat self-installer that makes it possible to install in user directory (so that it doesn't spread on the whole system).
What's more, this i386 version worked smoothly despite my amd64 kernel.
Right, the username. Someone who figured out I was French (a space slipped before a question mark) on a lisp-related IRC chan told me there were many French Lispers. I wonder how many we actually are.
In Paris too, and I'd be glad to meet some other lispers. Especially since I'm still in the early process of learning Lisp. But I guess we should find another way of dealing with IRL meetings than the forum...
Great! Thank you so much, I indeed missed the link on PLT's download page. I'll try it as soon as I'm back home (I can't wait). And thanks for the rlwrap tip too!
I also noticed this error back when I ported quasiquote from GNU clisp (http://arclanguage.org/item?id=9962). I was happy to find that the port fixed this bug when I similarly made a macro-defining macro where the newly-defined one took a rest parameter. Small world.
MzScheme has mzc, which does bytecode compilation. You can create something like standalone executables by embedding a copy of MzScheme into the module you're compiling. See http://download.plt-scheme.org/doc/372/html/mzc/ for more details.
(module test.arc "arc-exe-init.scm"
(require "ac.scm")
(namespace-require "ac.scm")
(require "brackets.scm")
(use-bracket-readtable)
(aload "arc.arc")
(aload "libs.arc")
(aload "test.arc") ; or the output of (acompile "test.arc") could work
)
Then
$ mzc --exe test test.arc.scm
mzc version 360, Copyright (c) 2004-2006 PLT Scheme Inc.
[output to "test"]
$ ./test
Hello, world
The results you get hardly seem worth the effort. With this, you'd still need to distribute with some form of ac.scm et al.
$ mv test ~/Desktop/
$ cd ~/Desktop/
$ ./test
default-load-handler: cannot open input file: "~/Desktop/ac.scm" (No such file or directory; errno=2)
=== context ===
#f::352: loop
There are probably smarter ways of compiling that don't have this hang-up, but in the end you'll still have kind of a large binary with MzScheme embedded.
If you just want the Scheme equivalent of Arc code, Arc already compiles down to Scheme. To get this compiled version (even if you can't run it directly without loading ac.scm, brackets.scm, arc.arc, and libs.arc):
$ cat test.arc
(prn "Hello, world")
$ mzscheme -m -f as.scm
Use (quit) to quit, (tl) to return here after an interrupt.
arc> :a
> (acompile "test.arc")
Hello, world
#t
> ^D
$ cat test.arc.scm
(ar-funcall1 _prn "Hello, world")
Not to worried about about mzscheme being embedded as it's only 24MB. This is good to know... I plan to experiment with it a little so thanks for guidance.
Not to clutter my own thread, but these are some issues that I wasn't smart enough to solve before publication. I felt only one of them qualified as a bug, so I posted it to a bitbucket issue tracker: http://bitbucket.org/fallintothis/arc-vim/issues/
Known Issues:
1. The way numbers are highlighted is mostly a hack. Arc, built atop mzscheme, currently inherits the Scheme reader. As such, numbers comply with R5RS syntax, plus some mz extensions. Highlighting is somewhat complicated: reals, rationals, complex numbers, binary, octal, hexadecimal, exponentiation, etc. The way I do it is to programmatically piece together giant, naively-built regular expressions from the R5RS EBNF grammar (modified slightly for mz). Then, each RE is noisier because I wrap them in delimiter REs so that numbers won't highlight when they're in names (like foldl1, 1up, or p2p).
This might make rendering slower versus simpler, naive alternatives (see Scheme & CL highlighting); I've not noticed anything terrible, myself. Having complex numbers & such highlight correctly is awesome, so I'm inclined to stick with the grammar route, if there aren't any major issues. There could be bugs in the way I generate the RE, which would be noticeable if valid numbers didn't highlight. Again, I've not noticed any (save for the ssyntax issue discussed later), but please tell me if you do.
2. I make overzealous use of display in the Vimscript, guessing at when it should be okay to use (the documentation isn't that clear). Not sure if there are bugs here, though they might also be mitigated with different synchronization methods. You'd notice this issue if, while scrolling through a file, Vim suddenly "runs out of colors" -- highlighting everything like a string or a comment, even though the region ended somewhere above your cursor. It's an easily overlooked problem (once you jar Vim into highlighting correctly), but one I don't like much: it's hard to replicate or know what's wrong. I've not noticed any problems like this for awhile, though.
4. The g:arc_bodops option requires that Vim is compiled with Python support because I couldn't figure out a way that wasn't convoluted to iterate through regular expression matches in a buffer. Seems gross to rely on Python, but I don't know how you can beat
for line in vim.current.buffer:
m = re.match(regex, line)
with Vimscript. Suggestions are welcome.
After this, it's worth noting that number (and ssyntax) highlighting is still pretty accurate. It correctly highlights the following, even though you might not be able to tell at first glance what the right behavior is supposed to be:
#e#x+e#s+e@-e#l-e ; a
16140901064495857664-50176i.#e#x+e#s+e@-e#l-e ; b
#b#e101010101111101010101201010101010000010101 ; c
127.0.0.1 ; d
+inf.0@3+4i ; e
+inf.0 ; f
1.+i ; g
_.1.3 ; h
16140901064495857664-50176.0i!#e#x+e#s+e@-e#l-e:+inf.0@1/1###.##0e4/2i+hi ; i
Whereas with the Scheme highlighter: (a) incorrectly rejects, (b) incorrectly accepts, (c) rejects for the wrong reason (more than one # mark), (d) incorrectly accepts, (e) rejects for the wrong reason (doesn't recognize mz inf constants), (f) rejects (doesn't recognize mz inf constants), (g) correctly accepts, (h & i) doesn't have ssyntax, unfair comparison.
These are just some of the tests I've run (many ripped off of mzscheme tests).
1. Vim's auto-indentation for Lisp is controlled by the &lispwords variable. e.g.,
; :set lispwords+=def
(def f (x)
body)
; :set lispwords-=list
(list x
y
z)
def forms have their arguments indented by the standard amount of space, while the arguments to list are lined up with each other.
&lispwords can be approximated by the names of macros that take a rest parameter named "body". It's not as strict as in Common Lisp, because it's just a naming convention instead of a keyword, but it's followed pretty closely in Arc's source. I just maintain a few exceptions to the rule.
But using &lispwords gives a pretty binary choice that fails on "wavy" indentation like
(if a
b
c
d
e)
and the indentation of paired forms like the variables of
(with (x something
y something-else)
body)
In all, a better equalprg would be really nice. Not something I plan on doing, but maybe some crazies out there feel like it.
2. Symbols like avg get highlighted in the deftem at the top of news.arc, even though it doesn't refer to the function avg. This can actually be really helpful, so that you know when you accidentally shadow a standard function/macro/variable. Besides, detecting this distinction in Vim seems too difficult.
3. Open question: should forms like a!b!c only highlight the exclamation points, or (current behavior) highlight past each ! as though they are quoted symbols? I think single exclamation marks like a!b should highlight past ! like a quoted symbol, since that's what the ssyntax means and it's quite readable, but nested ones will "run together" under such a scheme.
4. If, like me, you use Vim views & sessions, your old settings for existing Arc files may prevent
au BufRead,BufNewFile *.arc setf arc
from kicking in. You can setf manually, so that the view is saved with this info, or delete the existing views so that it stops reverting the filetype from (in my case) "arc" back to just "". Took me for-fucking-ever to figure that one out. Hope this helps someone as much as it would've helped me.
The 'li function could be simplified. html.arc has the facilities to define tags; coupled with 'tostring, we can capture the output, giving something like:
(attribute li class opstring)
(attribute li id opstring)
(def li items
(tostring
(each i items
(if (isa i 'table)
(tag (li class i!class id i!id) (pr i!body))
(tag li (pr i))))))
This also uses a rest parameter so that 'li can take any number of arguments and collect them up into a list:
arc> (li (obj body "hi" id "brown") (obj body "world" class "green") "foo")
"<li id=\"brown\">hi</li><li class=\"green\">world</li><li>foo</li>"
Though it doesn't quite replicate the use of 'string you had originally, e.g.,
arc> (li ''(a b c)) ; the one defined above
"<li>(quote (a b c))</li>"
arc> (li (list ''(a b c))) ; yours
"<li>quoteabc</li>"
You note that 'gentag prints all over the place. I'm sure (because of the game you built) you notice that Arc uses printing in its html-generation tools. So if you wanted to use this 'li function inside, say, a 'defop, you probably don't want to wrap it in the 'tostring -- instead, let it print so that you don't have to make the call to 'pr yourself.
I think the difficult part is passing in an arbitrary name to a function, for which the tables 'name' becomes set to, but I am just guessing that's what he wants...
I rather doubt that's the issue, as it's easy to do with a macro.
arc> (mac filltbl (tbl keys vals)
`(= ,tbl (fill-table (table) (mappend list ,keys ,vals))))
#3(tagged mac #<procedure: filltbl>)
arc> (filltbl blah '(a b c) '(1 2 3))
#hash((c . 3) (a . 1) (b . 2))
arc> blah
#hash((c . 3) (a . 1) (b . 2))
I think it's just a request for a convenience function to zip together a list of keys with a list of values into a table, since the existing methods for creating tables center around having alternating key/value pairs.