Arc Forumnew | comments | leaders | submit | jazzdev's commentslogin
3 points by jazzdev 4894 days ago | link | parent | on: Tips on implementing arc

I started Jarc by writing an s-expression reader. Then I implemented the 9 Arc primitives in Java and then I started writing eval in Java. Then I incrementally added the missing global functions used in arc.arc - that took a while, but wasn't particularly difficult.

I spent a lot of time thinking about the Arc to Java interface. I wanted it to be as light weight as possible and as brief as possible, in keeping with the Arc goal of making programs shorter. In particular, I wanted to avoid any 'import' or 'defcall' requirements if possible.

Another important consideration is how much of your interpreter can you write in Arc. I was anxious to stop writing Java and be able to add missing global functions in Jarc as soon as possible. At last count, I have 61 functions implemented in Java and 73 in Jarc itself.

Jarc doesn't have first-class continuations or fractional numbers. The interpreter also doesn't do tail call elimination. (I did write a compiler this year to do tail call elimination in recursive functions.)

From what I learned working on Arc and talking with Conan about Rainbow, implementing a continuation passing style interpreter is the way to go if you want first-class continuations and tail call elimination.

Writing Jarc has been great fun, and I've learned a lot.

-----

2 points by shader 4892 days ago | link

"I started Jarc by writing an s-expression reader. Then I implemented the 9 Arc primitives in Java and then I started writing eval in Java. Then I incrementally added the missing global functions used in arc.arc - that took a while, but wasn't particularly difficult."

That's how I planned to start the implementation, but I've also considered writing a compiler that translates the arc code into MSIL using Reflection.Emit. That would allow me to do tail-call optimization, and possibly CPS.

So what do you have currently for your Arc/Java interface, and what are your thoughts on difficulty of implementation, ease of use, etc. associated with that choice? Are there other options that you considered and discarded, or haven't gotten around to implementing yet? I was thinking of trying to treat assemblies, namespaces, classes, and objects like hash tables, and letting (obj 'name) access the member, but I don't know how well that will work with mostly static typing.

I'm not sure what to think about fractional numbers. I don't think I've ever had a good reason for using them, so I won't bother implementing them myself until I do.

-----

1 point by evanrmurphy 4893 days ago | link

> implementing a continuation passing style interpreter is the way to go if you want first-class continuations and tail call elimination.

I'm intrigued. Is this how most languages get tail call elimination?

-----

3 points by fallintothis 4893 days ago | link

Is this how most languages get tail call elimination?

Well, more languages do TCO than use CPS -- never mind a CPS interpreter. Though CPS is functionally equivalent to the more popular SSA (Static Single Assignment) form, it remains relatively unused; see http://lambda-the-ultimate.org/node/3467. I imagine the comment about using an interpreter is rooted in http://billhails.net/Book/v10.html, a great chapter from a book that goes through writing a Scheme-like interpreter in Perl, including a CPS transform and TCO using a trampoline. (For quick info about what those mean, there's of course http://en.wikipedia.org/wiki/Continuation-passing_style and http://en.wikipedia.org/wiki/Tail_call)

In fact, CPS benefits from TCO. By converting a program into explicit CPS, every function call becomes a tail call (to the current continuation); without TCO, stack space would run out quickly. Certain TCO implementations can also use CPS; see http://en.wikipedia.org/wiki/Tail_call#Implementation_method....

A great discussion about TCO implementation is http://lambda-the-ultimate.org/classic/message1532.html. To get really into it, the master's thesis at http://users.cecs.anu.edu.au/~baueran/thesis/ talks about tail calls in GCC (I've not read it, but it looks interesting).

-----

1 point by evanrmurphy 4892 days ago | link

Wish I could upvote you more than once. Great reply.

-----

2 points by jazzdev 4935 days ago | link | parent | on: Arc Conference

Seems like a dinner gathering on a weekend in the Spring or Summer in Silicon Valley might work.

How about Sunday June 19th or Sunday June 26th? Maybe 6pm?

I'm just picking some dates around the beginning of Summer as a starting point.

-----

1 point by jazzdev 4939 days ago | link | parent | on: Arc Conference

1. Where would you be willing to travel to?

Anywhere in North America (US, Canada, Mexico). Maybe Europe...

2. What presentations would you like to hear?

I'd like to learn more about Lathe, Rainbow, Semi-Arc and it would be interesting to hear pg talk about lessons learned from Arc so far.

3. Is there anything you would like to present?

I'd be happy to talk about Jarc (why I did it, what I've learned, etc.)

4. Does it have to be any particular time of the year for you to be able to attend?

Nope.

-----

1 point by jazzdev 4940 days ago | link | parent | on: The Dark side of Symbols

Wow, it's dark twisty passages all the way down.

More for the Jarc To-Do list. I can see Jarc has only begun to scratch the surface on escaped symbols.

-----

3 points by jazzdev 4940 days ago | link | parent | on: Pervasive keyword args for arc

I'm starting to view arc not as a language but as a thin membrane over an underlying lisp.

When I first read this I thought, "But what about arc on top of other languages beside scheme?" Then I realized I started writing Jarc because I wanted a thin membrane over Java. Interesting way to think about it.

-----

1 point by akkartik 4940 days ago | link

Yeah I started thinking this way after spending some time atop sbcl. I'll release that at some point. It's quite surprising how little code you need to build the arc transformer when you choose the right lisp implementation, when you're not going against the grain of the underlying lisp.

-----

3 points by jazzdev 4940 days ago | link | parent | on: Quoting function values

Ideally you wouldn't need to quote it at all.

It works in Jarc without quoting.

  Jarc> (eval `(,+ 1 2))
  3
  Jarc> (eval `(,(obj a 1) 'a))
  1
Not intentional. Just a side-effect of the way Jarc evals objects. Everything except cons, sym and string evals to itself.

-----

1 point by jazzdev 4940 days ago | link | parent | on: Jarc 21 Released

161 days ago you announced your jvm.arc? Interesting. Seems useful.

So Groovy allows dynamic classes, not just interfaces? I'd like to do that in Jarc also. But I don't see how to do it without generating byte code. Jarc now includes Jasmin (a JVM assembler) so generating byte code is pretty straight-forward.

Congrats on the job.

-----

1 point by rocketnia 4940 days ago | link

So Groovy allows dynamic classes, not just interfaces? I'd like to do that in Jarc also. But I don't see how to do it without generating byte code.

Right, making new classes that extend classes (besides java.lang.Object) at runtime seems to require generating byte code. From http://download.oracle.com/javase/6/docs/technotes/guides/sc...:

Sun's implementation of JDK 6 is co-bundled with the Mozilla Rhino based JavaScript script engine. (...)

Rhino's JavaAdapter has been removed. JavaAdapter is the feature by which a Java class can be extended by JavaScript and Java interfaces may be implemented by JavaScript. This feature also requires a class generation library. We have replaced Rhino's JavaAdapter with Sun's implementation of the JavaAdapter. In Sun implementation, only a single Java interface may be implemented by a JavaScript object.

In other words, the Rhino distributed with JDK 6 provides no JavaAdapter functionality the Proxy class can't already achieve....

Congrats on the job.

Thanks much! XD

-----

1 point by jazzdev 4941 days ago | link | parent | on: More Jarc and Rainbow bugs

I've fixed these Jarc bugs in Jarc 21. I would not have thought to try nesting optional args inside destructuring. Should work, though, of course. And actually the Jarc code is cleaner this way.

-----

1 point by jazzdev 4941 days ago | link | parent | on: Semi-Arc

Interesting. What problem are you trying to solve with the change to macro variable evaluation?

-----

2 points by rocketnia 4940 days ago | link

This hygiene problem, I believe:

  arc> (obj a 1 b 2)
  #hash((a . 1) (b . 2))
  arc>
    (let list list:list
      (obj a 1 b 2))
  #hash((((a 1 . nil) . nil) . ((b 2 . nil) . nil)))
Currently, (obj a 1 b 2) expands into (listtab (list (list 'a 1) (list 'b 2))), which means it uses the local meanings of 'listtab and 'list during evaluation.* The Arc community already removes much of the need for hygiene by following the convention of using (uniq) for local variable names generated by macros, but that doesn't help in this case.

Technically, we could fix this as a community too, as long as we adopt a convention like one of these:

- Have (obj a 1 b 2) expand into ('#<procedure: listtab> ('#<procedure: list> ('#<procedure: list> 'a 1) ('#<procedure: list> 'b 2))), for instance.

- Have (obj a 1 b 2) expand into (eval!listtab (eval!list (eval!list 'a 1) (eval!list 'b 2))), for instance, and avoid using "eval" as a local variable name.

- Avoid using existing global variable names as local variable names, and vice versa.

Whenever possible, I like being able to repair a REPL session just by fixing one buggy global binding, so I don't recommend the first option here. The third option is closest to what we have in practice, but I don't like the way the global/local distinction is hazy without introducing some kind of boilerplate naming convention. So the second option is kind of my favorite, but it still smacks of namespace qualification, and it doesn't help Arc code that already exists.

Semi-Arc takes a "none of the above" approach and just implements the hygiene, despite the incompatibilities that risks. I've only found one positive use of "free symbol capture" in practice,* so that probably won't be the top issue when porting code to Semi-Arc.

* IIRC, an example I wrote had one macro that bound a gensym-named local variable and another macro that referred to it using the same gensym. The second macro referred to the variable plainly enough that it was no more useful than an anaphoric variable, and it could just as well be replaced with a dynamic variable or a global variable. Nevertheless, it may turn out to be a useful technique for a variable that needs to be captured by lexical closures but only needs to be used for a specific purpose (incrementing it and broadcasting an update event, maybe).

-----

1 point by jazzdev 4939 days ago | link

Thanks, that's a great explanation. Makes sense.

The Jarc 'match' actually takes advantage of this lack of hygiene to implement this Perl-like idiom:

  (awhile (readLine in)
    (if (match "^\S") (prn it))
  )
Where match is defined to search 'it' if no 2nd arg is provided.

  (mac match args
    `(match-str ,(car args) ,(if (> (len args) 1) (cadr args) 'it)))
I thought about using the same pattern in the Jarc sql package to dynamically turn tracing of the SQL statements on and off. But it seemed inelegant making all the sql functions into macros just to get the dynamic tracing, so I decided to use thread-local variables instead in that case.

-----

2 points by rocketnia 4939 days ago | link

If you don't mind me suggesting...

  (mac match (regex (o subject 'it))
    `(match-str ,regex ,subject))
(Note that this rejects argument lists of lengths other than 1 or 2, whereas yours doesn't.)

And yeah, that is a good use of it. ^_^ An anaphoric parameter.

-----

1 point by jazzdev 4939 days ago | link

Thanks for the improvement. Much shorter and more readable that way. And error checking as you mentioned.

-----

2 points by jazzdev 4942 days ago | link | parent | on: Jarc 19 is out

I've been away from the forum (trying to get my startup launched). I'll look into the bug reports in http://arclanguage.org/item?id=12269 this week. Thanks for the bug reports, as always.

I've changed the default behavior for printing Java types that don't have a fn in writefns* -- Jarc now prints them as

  #package.class(hashCode)
It was getting annoying getting the missing writefns* messages. You can still define your own, but it doesn't pester you to do so anymore.

I'm working on a Rainbow compatibility module so that Jarc and Rainbow can share modules that call out to Java classes (like Swing and GAE datastore).

-----

More