Arc Forumnew | comments | leaders | submitlogin
Defcall
19 points by nex3 6112 days ago | 18 comments
One of Arc's neat features is the ability to use non-functions in functional position. This makes data access much more concise and simple.

Unfortunately, it's not possible to manually define how to handle user-defined types in functional position. This means that user-defined types are less powerful than built-in types, which we all know is a bad thing.

Thus, I've just pushed a small change to ac.scm in the Git wiki. When it goes to figure out how to call a non-function object, it no longer uses a big old hard-coded cond. Instead, it checks a table, call, defined and populated in Arc code. This table associates types with functions to use to call those types.

I've also added a little macro called "defcall" that's provides a nice interface for defining one of the functions. The syntax is

  (defcall type (obj . args)
    body)
As an example of how this works, I'll define a very simple 'alist type:

  arc> (defcall alist (al key)
         (alref al key))
  #<procedure>
  arc> (= al (annotate 'alist '(("foo" "A metasyntactic variable.")
                                ("peanut butter" "A tasty spread."))))
  #3(tagged alist (("foo" "A metasyntactic variable." . nil) ("peanut butter" "A tasty spread." . nil) . nil))
  arc> (al "foo")
  "A metasyntactic variable."
PG, I seem to remember you expressing interest in making this customizable - is this sort of what you had in mind?


6 points by absz 6112 days ago | link

Beautiful, nex3. Works exactly like I had been thinking about. But I'm not sure that making the table live in Arc is the best idea... it relies on a table of a certain name, which I think is slightly odd. Nevertheless, excellent--including the move of everything but functions into arc.arc. Exactly how it should work. Thank you.

Now, if I could only understand defset, I'd be all set... :)

-----

5 points by nex3 6112 days ago | link

I put the table in Arc because I was trying to make it work as closely as possible to stuff like defset and help. Although it is a little weird to have the core code relying on stuff going on in Arc-land, I think that's going to be necessary to give Arc as much power as possible.

Also, the core code only relies on call to be defined when it's actually trying to resolve a functional-position object. Doing something like (= call nil) will only fail when you actually try to use a non-function object as a function.

-----

3 points by absz 6111 days ago | link

That's a good point. And I do understand when it will fail, it just seemed odd. But you do make a good point about the power; it also reduces the number of axioms.

-----

4 points by CatDancer 6111 days ago | link

Think of it that the ideal Arc would be written entirely in Arc, except that some parts are written in Scheme for performance reasons or to allow something to actually run.

-----

7 points by kens 6111 days ago | link

I've been thinking that the Scheme code (ac.scm) should be split in two parts: an "axiomatic" part that defines car, cdr, set, annotate, etc; and a "library" part. The "library" part would define OS stuff like current-gc-milliseconds, open-socket, probably atomic-invoke, etc. There would probably be a "math" library that defines exp, sqrt, and all the missing math functions.

This structure would make it clear what parts of the language are really axioms, and what parts are in Scheme for performance, convenience, or because they are OS things that require low-level hooks.

-----

3 points by sacado 6111 days ago | link

"in Scheme for performance" : in never thought I would ever read that sentence :) (You're right, by the way)

-----

1 point by CatDancer 6111 days ago | link

Being able to call out to MzScheme from Arc would be cool. Then all the "library" parts could be written in Arc.

  (def file-exists (name)
    (scheme-istrue ((scheme-fn file-exists?) name)))
Here "scheme-fn" is a macro that returns an Arc function that calls the named Scheme function, but still expects to be passed Scheme values and returns a Scheme value. Then various functions such as "scheme-istrue" can be used to convert Arc values to Scheme values and back again.

-----

5 points by kens 6111 days ago | link

Anarki provides the $ operator, which allows callout to MzScheme.

I've been thinking that support for a foreign function interface such as SWIG would also be good.

-----

6 points by nex3 6111 days ago | link

Anarki has a macro, $, for just this. In general, I've taken to using $ for what kens refers to as the "library" part. Note, for example, files.arc.

-----

1 point by CatDancer 6111 days ago | link

Very nice!

-----

2 points by eds 6112 days ago | link

Great! This means infix.arc can now be defined entirely in arc without needing hacks in ac.scm. (Although this also means infix.arc is now dependent on defcall.)

-----

1 point by CatDancer 6111 days ago | link

Isn't

  (arc-eval '*call*)
simply

  _*call*

?

-----

4 points by nex3 6111 days ago | link

Not quite. If I just put _call, Scheme will try to resolve the variable at compile-time. However, it hasn't been defined by then, so an error is thrown. In order to allow it to be defined in-Arc, I had to wrap it in a call to arc-eval.

-----

1 point by CatDancer 6111 days ago | link

Hmm, I suppose you could initialize it in ac.scm... something like

  (define _*call* (arc-eval '(table)))
perhaps? (I don't know enough about arc-eval to know if that's correct).

But, I think I like your approach better.

-----

2 points by sacado 6111 days ago | link

(xdef 'call (make-hash-table 'equal)) is the right way to do in ac.scm I think

-----

2 points by eds 6111 days ago | link

Nitpick: isn't the arc naming convention for special variables call* not * call* (space used to prevent autoconversion to call)?

-----

2 points by nex3 6111 days ago | link

I chose "call" because that's how "help" was named. The setters hash is just "setters," though, I believe. I dunno, if you come up with a better name, feel free to rename it.

-----

2 points by eds 6111 days ago | link

Examples:

arc.arc:

  templates*, bar*, hooks*
srv.arc:

  arcdir*, logdir*, quitsrv*, breaksrv*, srv-noisy*, srvthreads*, threadlimit*, threadlife*, ...
etc.

I don't really care that much, (at least not enough to bother changing it), just thought we might want to follow pg's conventions (in so far as those conventions exist).

-----