kstrafe / Teko

Implementation of Teko in Rust
2 stars 1 forks source link

Thoughts on how to do documentation #2

Open teodorlu opened 7 years ago

teodorlu commented 7 years ago

It would be neat if documentation could be stored semantically (as code) instead of as text inside comments. Here's what the Clojure equivalent could look like:

(defn mymap [f x]
  '(:doc "Apply f over x"
    :examples ((11 12 13) (map (fn [x] (+ x 10)) '(1 2 3))
               (11 21 31) (map inc '(10 20 30))))
  ;; Defer to Clojure
  (map f x))

Basic thought: If test cases are stored on the function, live coding could be really neat. In the process of editing the function? Just show its test cases live, highlighting any that fail.

Inspired by Live-py.

teodorlu commented 7 years ago

I haven't really thought through how all that would work. I guess (since the expressoin is quoted), you could traverse the whole thing and look for the equivalent keys. Another take separating the information into two independent macros:

(defn mymap-2 [f x]
  (doc "Apply f over x")
  (examples ((11 12 13) (map (fn [x] (+ x 10)) '(1 2 3))
             (11 21 31) (map inc '(10 20 30))))
  ;; Defer to Clojure
  (map f x))

Backside is that evaluation may be more tricky. I guess both doc and examples are macros that don't do anything at run time, but should leave some data for other macros to pick up. Thoughts?

kstrafe commented 7 years ago

Functions are currently stored as Vec<Rc<Sourcedata>>, so it's possible to create a builtin function to extract the first element thereof. Here's what it'd look like:

(define mymap (fn (f x)
                  (quote (info Apply f over x)
                         (examples ((fn (x) (+ x 10)) (quote 1 2 3))))
                  (map f x)))

We can now extract the documentation:

(doc mymap)

Returns

(quote (info Apply f over x)
       (examples ((fn (x) (+ x 10)) (quote 1 2 3)))

Once this is done you can traverse and scout examples, and even run code, so we have live-testing of functions. One problem with this approach is that the language is dynamically scoped, so some tests could fail.

Now if the first element of the function is actually part of the function's implementation, we'll just return that data structure, I'm not sure if this is desired:

(define mymap (fn (f x)
                  (map f x)))
(doc mymap)

Returns

(map f x)

Traversing as you proposed seems really nice actually because it removes a pair of parentheses (which is (always (very)) nice)!

kstrafe commented 7 years ago

This is implemented as of version 0.1.9. Live documentation is run by implementing the appropriate function and running (eval (doc f)) since doc returns an arbitrary data structure, if there are no further comments I'll close this.