Arc Forumnew | comments | leaders | submitlogin
Check function or macro definition?
2 points by evanrmurphy 5567 days ago | 10 comments
Learning Arc I've found myself going back to "arc.arc" a lot to see how a function or macro is defined. I was wondering if there's a way to check such definitions from the REPL.

For example, say I'm trying to figure out the 'map1 function. Entering the symbol name tells me it's a procedure, but not what it does or how it works:

  arc> map1
  #<procedure: map1>
I'd like a hypothetical function (calling it 'defex here) that could print the definition of 'map1 as it is in "arc.arc", something like:

  arc> (defex map1)
  (def map1 (f xs)
    (if (no xs)
        nil
        (cons (f (car xs)) (map1 f (cdr xs)))))
Is there such a thing available or another way to address my problem? Thanks.


2 points by evanrmurphy 5567 days ago | link

I found this paragraph in the tutorial that I thought could be related:

  There's one thing you can't do with functions that you can do with
  data types like symbols and strings: you can't print them out in a
  way that could be read back in.  The reason is that the function
  could be a closure; displaying closures is a tricky problem.
Does this mean what I'm requesting isn't possible at the moment, or is it a different idea?

-----

2 points by shader 5565 days ago | link

This is a different idea. He's pointing out that in general functions can't be printed out, because the function could be represented by something very complicated and hard to print. His example, closures, are function objects that were defined in a lexical context that is relevant to their function. Merely knowing their source code won't be helpful, since two identical looking functions can behave very differently.

If you knew it was a closure, you could probably substitute the variables values for their names, and then print the resulting code, but that requires that you know both what the values are, and the fact that the function is a closure.

Also, I think he's describing the more complicated concept of reverse engineering the actual data type of a function, and printing a representation of it. Taking code that runs, and turning it into code you can read is not an easy challenge. I dare you to macex even a simple function. Taking that and turning it back into something you can read, with meaningful names, is impossible (I think).

At any rate, with 'src I took the shortcut of storing the source code in a table every time a function is defined. This has the disadvantage of only working for global, named functions defined with a small set of function creators (def and mac, mainly) since it uses a global name table. If it stored the string representing the code with the function object itself, it would probably be able to handle local, un-named functions as well, but would still only work with select function creators. This is mainly because you wouldn't want to see the crud added by defop over defop-raw, etc.

So, in general, displaying executable code in a human-readable format is an immense challenge. The more about the original function definition you keep around at run-time, the more easily you can represent it in a human readable form. The hack that is 'src achieves pretty function printing at the cost of storing the whole source code of global functions in a table at run-time. It still lacks a lot of information, and doesn't cover functions defined in local contexts, but it achieves at least a portion of the goal of exploratory, interactive programming.

-----

1 point by evanrmurphy 5561 days ago | link

Thanks for such a thorough explanation, shader. Gives me more to grok. :)

-----

1 point by shader 5560 days ago | link

Supposing you knew that a function was a closure, couldn't you print it out as

  (= fname
    (with (var1 val1 var2 val2 ...)
      (fn (arg1 arg2)
         ...)))
When read in, it should produce an identical closure. The problem is knowing under what variables the function is closed in the first place.

It would be nice if there was more information about objects and source code in arc. I.e. the source codes of functions, the current namespace, the variables captured in a closure, the stack trace, etc.

-----

1 point by evanrmurphy 5567 days ago | link

I'm finding posts now (e.g. http://arclanguage.org/item?id=9801) referencing an Anarki function 'src that may be what I'm looking for, but I haven't tried it yet.

-----

1 point by evanrmurphy 5567 days ago | link

I got Anarki and have been experimenting with 'src. It appears to be exactly what I was looking for:

  arc> (src map1)
  (from "arc.arc")
  (def map1 (f xs)
    (if (no xs)
        nil
        (cons (f (car xs))
              (map1 f (cdr xs)))))t
Handy that it prints the source file's name too. It even works for functions defined at the REPL:

  arc> (def testfn () (pr "this is testfn"))
  #<procedure: testfn>
  arc> (src testfn)
  (def testfn nil
    (pr "this is testfn"))t
'src will be very useful to me, and it seems the natural extension of the implementation-as-specification idea put forth in the tutorial:

  The definitions in arc.arc are also an experiment in another way.
  They are the language spec.  [...]
  It may sound rather dubious to say that the only spec for something
  is its implementation.  It sounds like the sort of thing one might
  say about C++, or the Common Lisp loop macro.  But that's also how
  math works.  If the implementation is sufficiently abstract, it
  starts to be a good idea to make specification and implementation
  identical.
Now I can access the details of the implementation--which is the specification--right from the REPL. Thanks, shader! (I'm presuming you're the author, in which case would you mind explaining the trailing 't printed on calls to 'src?)

-----

2 points by waterhouse 5566 days ago | link

I, my curiosity provoked, looked through Anarki, and I can answer your question: t is the value returned by 'src, by virtue of being the value returned by 'ppr. (A call to 'src expands to a call to 'ppr-source, and the last expression in 'ppr-source is (ppr source.name). (Actually it's source* but putting that in italicizes the rest of this message.)) It is 'ppr by itself that has this annoying property.

  arc> (ppr '(def meh (x) (list (+ x 2) (+ x 3))))
  (def meh (x)
    (list (+ x 2) (+ x 3)))t
It would be nice if ppr printed a newline at the end. In fact, I'm almost certain it should, because a) it seems one would use it to print code for human eyes, and nothing else, b) readable code does not generally have separate expressions on the same line, and c) anything printed on one line is by definition not prettily formatted and can be pr'd instead of ppr'd.

I would like to simply redefine ppr, thus:

  (let old-ppr ppr (def ppr args (apply old-ppr args) (prn))
Unfortunately, ppr is apparently defined in terms of itself. And when recursive functions get redefined, the calls to what used to be themselves get redefined too. Using the above makes ppr print out way too many newlines:

  arc> (src defop)
  (from "srv.arc")
  (mac defop
  
       (name parm . body)
  
       (w/uniq gs
  
               (quasiquote (do (wipe (redirector* '(unquote name)
  ...
It's just horrible. Simple hack fix: go to pprint.arc, replace all instances of "ppr" with "ppr-fn", and then add (def ppr args (apply ppr-fn args) (prn)). Or just put up with the t.

-----

3 points by shader 5566 days ago | link

Yep, the trailing t and lack of newline are due to ppr.

Fortunately, I also happen to have an alternative version of ppr on anarki, under the lib folder, which has just been updated to handle multiple expressions and print newlines.

Just pull anarki again, and use

  (load "lib/ppr.arc")
It should redefine sp, len, and ppr, and make source code printing much more readable than the pprint.arc version ;)

-----

1 point by akkartik 5567 days ago | link

Looks like it didn't make it across to the arc3 branch (http://github.com/nex3/arc/blob/arc2.master/arc.arc) but it's easy enough to port.

Update: I stand corrected; it is indeed in anarki: http://github.com/nex3/arc/blob/master/load/help.arc. Shader, is this the version you wrote?

-----

1 point by shader 5566 days ago | link

If it's not exactly what I wrote, it's still probably the correct version to use. I pushed my code to anarki but rntz handled the porting of most libraries to arc3. Any changes are probably for the better.

-----