Polyadic
Functions in Qi
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
dr.mtarver@ukonline.co.uk
|