| Mark Tarver 2007-06-28, 7:07 pm |
| People have written in to me asking 'Can you define polyadic functions
in Qi and use them in a type secure way?' The answer is 'yes you
can'.
This example uses a really simple type secure HTML formatter for you
to extend. It is based on a 1-line polyadic function defined in CL.
(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.
This post is study #8 in 'Studies in Qi'
(http://www.lambdassociates.org/studies/study08.htm)
Mark
* Note generally its 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.
|