People have
written in to me asking 'Can you define polyadic
functions in Qi and give them types?'. The
answer is 'yes you can'.
This example shows a really simple type secure HTML
formatter which you can extend. It uses a 1-line polyadic
function defined in CL. The basic command is (DEFUN html (Tag &REST Text) (FORMAT
NIL "<~A> ~{ ~A~} </~A>" Tag Text
Tag))
Thus
(html html
(html head
(html title "Test"))
(html body
(html p "Just some text")))
generates basic HTML
"<html> <head>
<title> Test </title> </head>
<body> <p> Just some text </p>
</body> </html>"
The type theory is more complex.
html is a polyadic function that can assume n arguments
where n > 1. In such a case we need to define a
special type theory for html. Here it is
(datatype html
Tag : symbol; String : string;
_______________________________
(html Tag String) : string;
if (cons? HTML)
if (= (head HTML) html)
if (> (length HTML) 3)
let String (nth 3 HTML)
let NewHTML [(nth 1 HTML) (nth 2 HTML) | (tail (tail
(tail HTML)))]
String : string; NewHTML : string;
_________________________________
HTML : string;)
The first (base) case is where n
= 2 - it says that html accepts a symbol and a string and
outputs a string.
The second case deals with n > 2 - it says that html
returns a string just when the second argument is a
string and the expression that results from removing this
argument also returns a string. This recursively shortens
the expression towards the base case.
Since html is a polyadic function and has no type of and
by itself, we do not support currying for it. This means
in Qi-speak it is a *special form* - one that
does not support currying. We need to tell Qi not
to try to curry this form of expression and to treat it
as special. This does the trick.
(specialise html)
So if I put all this in a file
"html.txt"
(DEFUN html (Tag &REST Text)
(FORMAT NIL "<~A> ~{ ~A~} </~A>"
Tag Text Tag))
(specialise html)
(datatype html
Tag : symbol; String : string;
_______________________________
(html Tag String) : string;
if (cons? HTML)
if (= (head HTML) html)
if (> (length HTML) 3)
let String (nth 3 HTML)
let NewHTML [(nth 1 HTML) (nth 2 HTML) | (tail (tail
(tail HTML)))]
String : string; NewHTML : string;
_________________________________
HTML : string;)
I can enter (load
"html.txt") in Qi and load the lot.* I
can now use it in a type secure Qi environment.
(24+) (html html (html head
(html title "Test"))
(html body (html p "Just some text")))
"<html> <head> <title> Test
</title> </head> <body> <p> Just
some text </p> </body> </html>" :
string
Once you've got this far you can
do other things - like creating your own type secure
formatting shorthand in the style of CL-WHO
(define :head
{string --> string}
Text -> (html head Text))
(define :title
{string --> string}
Title -> (html title Title))
(define :p
{string --> string}
Para -> (html p Para))
So
(html html (html head (html
title "Test")) (html body (html p "Just
some text")))
becomes
(html html (:head (:title
"Test")) (html body (:p "Just some
text")))
If you want to go further and
really make it tight you can replace
Tag : symbol; ....
in my datatype by
Tag : tag; ....
and define the type tag to
ensure that users cannot generate garbage HTML with
nonsense tags. But you've probably got the idea by now
and I'll leave the rest to you.
* Note generally it is smart to put Qi and Lisp
in seperate files and use the Qi 'load' to load Qi
and the CL 'LOAD' to load the CL because the readers are
slightly different. Here we can get away with mixing. It
is important to declare html to be a special form before
we declare the datatype otherwise Qi will use
currying in the datatype definition.
Mark
Copyright
(c) 2007, Mark Tarver
|