(def! reduce (fn* (fn acc l) (if (empty? l) acc (reduce fn (fn acc (first l)) (rest l)))))

(def! join (fn* (l) (reduce str "" l)))

(def! tag (fn* (name content) (join (list "<" name ">" (join content) "</" name ">"))))

(defmacro! mktags (fn* (& names) (map (fn* (name) `(def! ~(symbol name) (fn* (& content) (tag ~name content)))) names)))

(mktags "html" "head" "title" "body" "h1" "p" "ul" "li")

(print
  (html
    (head (title "Hello world!"))
    (body
      (h1 "Hello" "world!")
      (p "Writing from inside mal (lisp) seems to work, yeah?")
      (apply ul (map li '("one" "two" "three")))
    )
  )
)

Phew, that took a while!

As part of the whole "programmable site" thing, I've been wanting to write html-as-lisp (because it's fairly easy to type brackets and quotes on my phone keyboard, at least compared to other syntax).

I've been able to do the basic version for a while - use a macro to make functions that take their content, convert it to strings and wrap it in tags. The problem has been that when I generate content on the fly (e.g., the li tags above), the brackets from the list end up embeded in the outcome.

Until now! Because apply flattens it's arguments, I can use it to convert the list result of map into the arguments of ul.

Sweet. That should be a bit of incentive to get more work done on mal-sharp.


(def! tag (fn* (name content) (str "<" name ">" (apply str content) "</" name ">")))

(defmacro! mktags (fn* (& names) (map (fn* (name) `(def! ~(symbol name) (fn* (& content) (tag ~name content)))) names)))

(mktags "html" "head" "title" "body" "h1" "p" "ul" "li")

(print
(html
  (head (title "Hello world!"))
  (body
    (h1 "Hello" "world!")
    (p "Writing from inside mal (lisp) seems to work, yeah?")
    (apply ul (map li '("one" "two" "three")))
  )
))

Turns out that I don't need join or reduce, since apply str does the same thing. Neat.

Also, I'm still not sure what I'm going to do about attributes. I want to be able to do something like (html {"lang" "en-gb"} ...), with the hash argument being optional, but I need to work out how to do that. (and clearly, two versions of tag, and an if in the macro to pick the right one, but I'm still not wild about it).


The mal interface still isn't right (ignoring the bug that means it stops working when I tab away).

I've got a rough idea of what I want, which is clicking "Exec" takes a copy of the textarea and adds it to a history along with the result (either whatever result was printed, or the details of whatever error was thrown), and probably a "reload" button.

I'll mess about with it sooner or later.


To remember your current position in the blog, this page must store some data in this browser.

Are you OK with that?