[plt-scheme] use web service from scheme?

From: David Van Horn (dvanhorn at ccs.neu.edu)
Date: Sun Jan 25 15:52:56 EST 2009

Sigrid Keydana wrote:
> Hi,
> 
> I'd like to ask if there is a tutorial or example of how to use a web
> service from scheme? (Already saw flickr.plt on Planet, but a tutorial
> would be even more helpful of course :-) )

I'm not sure this counts as a tutorial, but here is a lightweight way of 
interacting with a simple web service, delicious.

The delicious web service basically offers you a number of functions, 
which you call by visiting certain URLs.  The path part of the URL 
determines which function you call, and arguments, which are named, are 
given in query part.  Results are returned as XML.

You can call these functions without using Scheme at all either by using 
a web browser, or a command line program like curl, or wget.  For 
example, change user:pass appropriately, and visit:

    https://user:pass@api.del.icio.us/v1/posts/recent?tag=scheme

You get an XML document listing all you bookmarks tagged with "scheme". 
  The function in this case is "posts/recent" and the "tag" argument is 
"scheme".

With this example, I get:

<?xml version="1.0" encoding="UTF-8"?>
<posts user="dvanhorn" tag="scheme">
   <post href="http://plt-scheme.org/"
         hash="4ffc3abae9db52de059f66944ef834f9"
         description="PLT Scheme" tag="scheme plt awesome"
         time="2009-01-25T19:32:05Z"/>
</posts>
<!-- fe04.api.del.ac4.yahoo.net uncompressed/chunked Sun Jan 25 12:24:23 
PST 2009 -->

Moving into the Scheme world, suppose we have a function that takes one 
of the designated delicious function names and named arguments.  A 
natural way to model named arguments is with keywords.  Rather than 
return an XML document, let's return an S-Expression representing the 
XML result (an X-Expr).

The example becomes:

(delicious "posts/get" #:tags "scheme")

Which evaluates to:

(posts
  ((dt "2009-01-25T08:00:00Z") (tag "") (user "dvanhorn"))
  (post
   ((description "PLT Scheme")
    (hash "4ffc3abae9db52de059f66944ef834f9")
    (href "http://plt-scheme.org/")
    (tag "scheme plt awesome")
    (time "2009-01-25T19:32:05Z"))))

This is just a plain ol' list in Scheme.  You can use all your familiar 
list functions to operate on these values.  You can construct new values 
with quasiquote and friends.  Generating HTML pages or RSS feeds are 
really simple exercises (compare this to just about any other 
programming language).

Here are some more examples:

(delicious "posts/update")
(delicious "posts/recent")
(delicious "posts/dates")

(delicious "posts/add"
            #:url "http://plt-scheme.org/"
            #:description "PLT Scheme"
            #:extended
            (string-append "PLT Scheme is an innovative programming "
                           "language that builds on a rich academic "
                           "and practical tradition.")
            #:tags "scheme plt awesome")

The details between each web service will vary (flickr, for example, has 
a much more complicated authentication scheme), but more or less they're 
all variations of the above.

Ask if you have more questions.

David


;; Lightweight delicious API wrapper.
;; For a more complete solution see planet: untyped/delicious.
#lang scheme
(require scheme/system
          net/uri-codec
          xml)

(define usr (make-parameter ""))
(define pwd (make-parameter ""))

;; A MethodName is one of:
;; - "posts/update"
;; - "posts/add"
;; - "posts/delete"
;; ...

;; MethodName [Keyword Arg] ... -> S-Expr
;; Invoke the given method with the given arguments.
;; http://delicious.com/help/api
(define delicious
   (make-keyword-procedure
    (lambda (kws kw-args method)
      (let ((in (fetch (delicious-url method kws kw-args))))
        (xml->xexpr
         ((eliminate-whitespace '(posts tags dates bundles)
                                (lambda (x) x))
          (document-element (read-xml in))))))))

;; String -> InputPort
;; Fetch a URL (represented as a string) as a port.
(define (fetch str-url)
   ;; Alt: (get-pure-port (string->url str-url))
   (match (process (string-append "curl \"" str-url "\""))
     [(list in out pid err send)
      (begin (send 'wait)
             in)]))

;; MethodName [Listof Keyword] [Listof String] -> String
;; Construct a delicious API URL.
(define (delicious-url method args vals)
   (format "https://~a:~a@api.del.icio.us/v1/~a?~a"
           (usr)
           (pwd)
           method
           (interpolate-&
            (map (lambda (s1 s2) (string-append s1 "=" s2))
                 (map keyword->string args)
                 (map uri-encode vals)))))

;; [Listof String] -> String
;; Interpolate the list of strings (list s1 s2 .. sN) as "s1&s2..&sN".
(define (interpolate-& los)
   (cond [(empty? los) ""]
         [(empty? (rest los)) (first los)]
         [else (string-append (first los)
                              "&"
                              (interpolate-& (rest los)))]))


;; Examples

(usr "dvanhorn")
(pwd "********")

(delicious "posts/update")
(delicious "posts/recent")
(delicious "posts/dates")

(delicious "posts/add"
            #:url "http://plt-scheme.org/"
            #:description "PLT Scheme"
            #:extended
            (string-append "PLT Scheme is an innovative programming "
                           "language that builds on a rich academic "
                           "and practical tradition.")
            #:tags "scheme plt awesome")

(delicious "posts/get"
            #:tags "scheme")



Posted on the users mailing list.