Arc Forumnew | comments | leaders | submitlogin
Modules, Multimethods (Kinda), and More for Arc (rocketnia.wordpress.com)
5 points by rocketnia 5124 days ago | 11 comments


2 points by rntz 5124 days ago | link

You mention in the article a 'setforms entry for 'global, and say that you need to do the equivalent of (eval `(= x ,something)), but that something can't refer to anything not in the global environment, and therefore you need another global to "ferry" the value over. This is not necessary. You can just compute the value locally and bind it to some variable, let's say foo, and then (eval `(= x ',foo)).

Also, you mention that if 'my is a module, then (my:foo a b c) will expand to something like (gs1234-foo a b c), and this is useful because (my.foo a b c) expands to ((my foo) a b c), which the arc compiler won't understand is a macro invocation even if gs1234-foo is bound to a macro. I encountered precisely this problem when trying to build a module system, and I solved it by making arc try to macroexpand lists in functional position, and then re-macroexpand if the result was a symbol -- which allows (my.foo a b c) to work. See http://arclanguage.org/item?id=7448, although the pastebin link in the grandchild has since expired. I'm not sure whether I committed this change to anarki, and whether, if I did, it survived the transition to arc3. It's pretty simple to do, though.

-----

2 points by rocketnia 5123 days ago | link

(eval `(= x ',foo))

Oh yeah, that does the trick. At one point I was considering doing that in my macros for hygiene, but then I realized I preferred hackability to hygiene so my code could be consistent with arc.arc, so I abandoned that idea... and somehow forgot you could even do that. XD So that should make the implementation of 'global a bit tidier--maybe even a bit less necessary.

making arc try to macroexpand lists in functional position, and then re-macroexpand if the result was a symbol

Yeah, I considered that, but as long as I could make namespaced macros work without resorting to a patch, I figured I should do that instead, since it made the code more flexible to whatever Arc implementations and patch configurations people wanted to use.

-----

2 points by jazzdev 5124 days ago | link

since Arc has no obvious and extensible type system to dispatch on...

Do you mean static type system? Because Arc has an extensible type system.

  arc> (type 'foo)
  sym
  arc> (type (annotate 'zero 0))
  zero
Jarc uses these types to implement multimethods when calling Java methods (which in Java is called function overloading). So Jarc figures out whether to call java.lang.Integer.valueOf(int) or java.lang.Integer.valueOf(String) based on the argument types.

  Jarc> (java.lang.Integer.valueOf 1)
  1
  Jarc> (java.lang.Integer.valueOf "1") 
  1

-----

1 point by rocketnia 5123 days ago | link

The 'type function (along with 'annotate) is what I considered the "obvious" type system, but I don't think of it as being extensible, at least in common practice. For instance, the result of 'testify is a special kind of function, but its type is just 'fn. The type of a queue is just 'cons. A 'setforms entry needs to return a special kind of value... but the type of that value is 'cons too. So while a multimethod system might be built up on Arc's tagged values, I don't know how useful that would be.

As for Jarc--which I haven't tried yet--sure, Arc strings acting as Java strings must be nifty. I suspect that makes Jarc Arc-to-Java calls work quite a bit like Groovy, with dynamic rather than static dispatch? Sounds like a plus to me.

Speaking of Jarc, I intend to test out the Lathe code on Jarc at some point, but for the time being I'm sure at least the multivals won't work, thanks to the way 'self-orderer-reducer (the reducer for 'order-contribs) uses continuation-based backtracking to do its permutation search.

-----

1 point by akkartik 5093 days ago | link

I think the solution to the type problems is to change arc to use tags more pervasively. No reason to be faithful to PG's implementation. Queues should be tagged 'queue, and so on.

-----

1 point by jazzdev 5123 days ago | link

Yeah, the continuation-based backtracking won't work in Jarc. :-(

-----

1 point by rocketnia 5119 days ago | link

I just re-implemented 'self-orderer-reducer so that it doesn't use continuation-based backtracking. :D I wanted to do that sooner or later anyway, just so that I could port Lathe libraries over to continuation-free languages if and when I wanted to.

In order to do this, I ended up porting an iteration library I'd written from Groovy to Arc, and then the problem was still hairy enough that I had to write Groovy pseudocode for what I wanted and port that too. Not counting the libraries each version needs (amb.arc and iter.arc), the new version is more than twice the size of the old one, and I'm sure it has more than twice as many fiddly bugs I haven't found yet. But hey, no continuations.

I'll try it out on Jarc later today. There are other things like reliance on tail-call optimization that might get in the way, but I'm generally hopeful.

-----

3 points by rocketnia 5118 days ago | link

It turns out supporting Jarc is way more challenging than I expected, and I'm going to give up for now. I pushed what I had to a new branch--woo, first time branching!--and it incorporates workarounds for quite a few idiosyncrasies of Jarc:

- Jarc doesn't have re-invokable continuations. As I mentioned earlier, I'm not relying on those for most of Lathe right now.

- Jarc has no 'get, despite having '.a and '!a ssyntax that expand to 'get forms. So I defined 'get myself, complete with an extension of setforms to make (= ((get a) b) c) work the way it works in Arc 3 and 3.1.

- Jarc has no 'a&b ssyntax, and the implementation of 'andf returns t on success rather than passing on the return value of the last function. So I defined 'doandf and used that instead.

- Jarc has no 'assign; it's named 'set instead. So I defined an 'xassign macro that could expand to the correct special form on Jarc or otherwise.

- Jarc has no (let (first . rest) lst ...) destructuring. I stopped using that.

- Nested quasiquotes don't work the same way in Jarc. I stopped using them.

- Jarc's 'compose apparently isn't a metafn. The symptom is that if I'm using 'a:b ssyntax where a or b is the name of a macro or special form, the arguments are still evaluated. I stopped using 'a:b syntax in these cases, and that was painful, since it gave every '(my:foo a b c) form an additional set of parentheses. I should have defined another macro to save me from adding those parentheses, but it didn't cross my mind until now.

Then I came across these two quirks in macro expansion:

  ; In Jarc, macros in subexpressions of function bodies are not
  ; expanded when the functions are compiled; instead, they're expanded
  ; when the functions are called.
  
  (mac foo () 1)
  (def bar () (foo))
  (mac foo () 3)
  (let foo (annotate 'mac (fn () 4))
    (bar))  ; results in 1 everywhere I've tested
  
  (mac foo () 1)
  (def bar () (idfn (foo)))
  (mac foo () 3)
  (let foo (annotate 'mac (fn () 4))
    (bar))  ; results in 3 on Jarc, but results in 1 on Arc 2 and others
  
  ; In Jarc, the macro expansions delayed this way are expanded
  ; according to the macros bound in their lexical environment rather
  ; than those in the global environment. I presume that this is an
  ; intended feature and that the other quirk exists mainly to let this
  ; work.
  (mac foo () 1)
  (let foo (annotate 'mac (fn () 2))
    (def bar () (foo)))
  (mac foo () 3)
  (let foo (annotate 'mac (fn () 4))
    (bar))  ; results in 2 on Jarc, but results in 1 on Arc 2 and others
The first quirk means my namespace macros almost never work from inside functions. Suppose I write something like this:

  (using-rels-as ut "utils.arc"
    
    (def my.fn-something (x)
      (- x 1))
    
    (mac my.something args
      `(,my!fn-something ,(apply ut.arg-counter args)))
    )
This won't macro-expand my!fn-something or ut.arg-counter until the 'something macro is invoked, by which time 'my and 'ut might be bound to completely different values.

I tried following along with these quirks so that when Lathe was running on Jarc, 'my and 'ut would be bound lexically rather than globally... but then I discovered that 'setforms doesn't expand places based on their lexically bound macros. I think 'setforms must call 'macex from within its own lexical environment, thereby neglecting any macros scoped in the expression's lexical context. And since all of my own uses of 'macex suffer from exactly the same problem, I've finally run out of steam.

For now, I'm just going to work on something besides Jarc support unless somebody here has a better idea. ^_^

-----

1 point by jazzdev 5110 days ago | link

Wow. Thanks for the laundry list. Most of the missing things are probably because I haven't moved Jarc from arc2 to arc3 yet. That's why assign is still called set also. I do plan do this. Probably as soon as I get the compiler working.

The macro expansion was just an expedient. Toplevel macros in functions are expanded at closure-time because it was trivial to implement. I wasn't sure how to do general expansion of all macros without a compiler, but I realize now, it's fairly straight-forward (you just have to know the semantics of the special forms). So I can envision a fix for this now. I never realized that delaying macro expansion would change the semantics (due to different lexical environment at expansion time), that seems pretty bad. I should probably look at fixing this before finishing the compiler.

-----

2 points by jazzdev 5107 days ago | link

I've fixed the most egregious problem, that of macro expansion timing in Jarc 10, released today.

I do plan to address the rest of these issues also in the next week or so. I appreciate all the info on Jarc incompatibilities with Arc. I wasn't aware of all of them.

-----

1 point by rocketnia 5090 days ago | link

Whoa, I only just noticed that 'global has exactly the same appearance and intended effect as almkglor's 'symeval (http://arclanguage.org/item?id=7701), except with a different name (albeit one aw suggested!) and, well, a different implementation and possibly some corner-case behavior differences. Necessity is the mother of reinvention, I guess. :)

-----