"I have a mind like a steel... uh... thingy." Patrick Logan's weblog.

Search This Blog

Friday, August 19, 2005

Emacs of the Web

Bill de hÓra contributes to the "one click subscription" discussion, or what he dubbed "clicksub"...

I don't use browsers much anymore and will be using them even less next year. Aggregators are so much better than browsers for following content. Really, if you have to read stuff on the web and are using a browser for that, you should try an aggregator. And then, what's the browser good for?...

To be honest, next year's browsers need to be aggregators, else I don't see the point in using them...

Instead this would be great: at some point weblogs flip over and the HTML website bits will become secondary fluff to the XML content, like how PDFs are secondary web fluff to HTML today. The frontpage would be the feed, the archives would be Atom entries...

Honestly, permalinking to a html file is starting to look more and more like a bug. Why not point to the XML entries?

I was going to blah blah about some analogies with Emacs. Hard-core emacs users find themselves snickering about artificial boundaries around "applications" -- there is very little concept in the world of Emacs that "this thing is a word processor" but "that thing is an email reader" and "some other thing is a directory explorer". And the list goes on for outliners, class browsers, PIMs, etc.

The underlying problem may be there is no (not yet) Emacs of the Web. But Bill what I get from Bill's post is my Emacs perspective is mostly a "front-end perspective". There is a whole back end perspective on the future of feeds, pages, formats that is in its infancy.

Emacs was born as a set of flexible front-end parts in the 1970's. Even in the world of objects and MVC, a significant number of programmers were just starting to grasp the same concepts in the late 1980's and early 1990's. By and large, looking at our software landscape, we still have not grasped the concept. In fact, the browser may be the closest thing we have to this on a widespread basis!

Not only does Bill show this is not seriously the case, by now we really should have moved on to the back end of resources, formats, and hyperlinks. The head spins. We have a lot of catching up to do.

Thursday, August 18, 2005

When to create syntax in Lisp?

From the gambit email list, I respond to this question about writing macros...

In your opinion, is it appropriate to use a macro to abstract away repetitive boiler-plate code? Or is this better done in a procedure?
This is almost always a procedural abstraction rather than syntax, especially for beginners with Lisp... better to spend a lot of time with just procedural abstraction, higher-order functions, etc.

Syntactical abstraction I use for controlling the order of evaluation and sometimes to put a pretty syntax around use of lambda.

As an example an old Lisp control structure is called PROG1. This structure takes a sequence of statements, evaluates each in order, and returns the result of the first statement after the last has been evaluated. This is can be expressed as a Gambit macro, but I'll call it begin1 to be more like Scheme's begin than the old Lisp's progn.

? (define-macro (begin1 first-statement . remaining-statements)
    (let ((result-var (gensym 'first-result)))
      `(let ((,result-var ,first-statement))
         , at remaining-statements
         ,result-var)))
? (begin1 1 2 3)
1
> (begin 1 2 3)
3
? (define result-var 5)
? (begin 1 2 result-var)
5
? (begin result-var 1 2 3)
3
? (begin1 result-var 1 2 3)
5
? (begin1 0 (display result-var) (newline) 1 2 3)
5
0
The begin1 is an example of control abstraction.

An example of the latter use of pretty syntax... consider a scenario where you are using a resource and you want some "before" and "after" actions. The base level way to implement this is with higher-order procedures. But who wants to write (lambda () ...) all the time? So on top of this build some pretty syntax. [Note that a better lambda notation like Smalltalk's block syntax would reduce the need for these situations. (pdf)] For example...

(define (call-when-ready procedure)
  (wait-until-ready time-out)
  (if (not (ready?))
      (call-when-ready procedure)))
Use it like this...
(call-when-ready 
  (lambda () 
    (display "I am glad this is finally ready!")
    (newline)
    (do-something)))
This is fine when someone else is generating the code for you. Normally you might want to abstract the procedure as a sequence of statements...
(when-ready
  (display "I am glad this is finally ready!")
  (newline)
  (do-something))
And so when-ready is defined as a macro that calls call-when-ready...
(define-macro (when-ready . body)
  `(call-when-ready
     (lambda ()
       ,@body)))
Will Farr added a neat example in the same thread...
Making little sub-languages for specialized processing e.g.

(with-vectors (v1 v2 v3) (v1 <- (+ v2 v3)))

for summing up the vectors v2 and v3 and storing it in v1. (I'm not going to put this macro up because it's long---the code is buried in this post: http://wmfarr.blogspot.com/2005/06/bigloo-macros-and-repl.html .)

[This] involves changing the evaluation rules for a piece of code (the vector assignment is evaluated once for each index with the corresponding variables bound to elements of a vector).Procedures would not work for either of them; you have to have a macro.

Monday, August 15, 2005

Because

Because the world is round...

Identities, Associations, and Behaviors

Doc Searls has the lead article in the September Linux Journal on "Identity". This is yet another topic I have barely understood. My interest is growing though with the concept of "user centric identity".

The title of Searl's article is "Independent Identity" which points out an irony of sorts that's been on my mind. My impression is that this is not about "identity" at all. Rather it is about "association".

What use is an independent identity? I have to associate my identity with some other body's identity to accomplish anything. The crux of the "user centric identity" effort is how to limit the ramifications of that association.

The other thought that's been brewing is the relationship between capabilities and identity. We know our current approach to permissions is not sufficient to limit or even audit the results of association. On the other hand if I can reliably limit the available behaviors of my associates, and they mine, then we can mutually benefit from our association.

Identity without behavior does not lead to associations. Since we are after beneficial associations, I'm assuming we need to focus on how "identity management" ultimately translates into appropriate behaviors and reliably excludes inappropriate behaviors. A capability system requires identity management to communicate with the outside word. I also assume an identity management system requires capabilities or something equally secure to ultimately implement trustworthy associations.

I'm looking forward to Phil Windley's new book on Digital Identity to get a better handle on some of these ideas.

Sunday, August 14, 2005

DZ

and now, daily zen...

By sitting alone all day long
I clear my mind of a thousand thoughts.
To speak of this is beyond our words;
Only by sitting under the quiet forest
Can we ever understand.

- Fa Yen (885-958)

uuid

Termite currently has a dependency on libuuid to generate universally unique identifiers. Since there is no need for the result to conform to the uuid_t data type, just that the result is unique across space and time, I wrote a uuid generator in Scheme which will make running Termite easier on more platforms. I loosely followed the pattern for generating primary keys in EJB Design Patterns which does not require a singleton or a database, etc. i.e. multiple instances of this uuid maker can exist in the same address space or in different address spaces, and on different hosts. I had fun replacing the absolutely horrible C code from libuuid with short, readable, Scheme.

> (load "my-uuid.scm")
"/home/pdlogan/dev/termite/my-uuid.scm"
> (define make-uuid (make-uuid-maker))
> (make-uuid)
"1A10E03B-4FF3486B-00000057-F7CA93A2"
The result is a 128-bit unsigned integer but the only requirement is that it is a unique Scheme object across space and time (within a reasonable amount of certainty). The code is actually following the convention for uuid's, i.e. display each 32-bit part in hex separated by dashes.

Each call to make-uuid-maker returns a new closure around an "address part" and an "object part". Every uuid this closure makes will be unique to its IP address and unique to the internal id of the object in the specific Scheme runtime. (Note: the original uuid generators use the network card MAC address but the IP address is easier to get and just as good for this purpose.)

Each call to one of these closures combines its address part and object part to a "time part" and a "random part". The time part is roughly the number of milliseconds since January 1, 1970 mod (2^32)-1. The random part is a random 32-bit unsigned integer. So within the closure's IP address and randomly allocated object, each resulting uuid is also made unique to the millisecond and a random number *within* the millisecond.

(define (make-uuid-maker)
  (let ((address-part (or (hex-encode-ip-address)
     (hex-encode-random-integer)))
 (object-id-part (hex-encode-random-object-id)))
    (lambda ()
      (let ((time-part  (hex-encode-current-milliseconds))
     (random-part (hex-encode-random-integer)))
 (string-append time-part "-" address-part "-" object-id-part "-" random-part)))))

The IP address is the host's internet address if it has one, or an intranet address, otherwise the address part becomes just another 32-bit random integer.

(define (hex-encode-ip-address)
  (let* ((address (or (get-internet-address)
        (get-intranet-address))))
    (if address
 (lengthen-with-leading-zeros
  (number->string (string->number
     (let ((result ""))
       (for-each
                                (lambda (n)
       (set! result (string-append result (number->string n))))
    (u8vector->list address))
       result))
    16))
 #f)))

The object id closed within a uuid maker is also random. If a closure is always created at a specific point, e.g. in initialization, then this object runs the risk of having the same id each time. Instead a random number of objects (not more than 100) are created before the one captured in the closure.

(define (hex-encode-random-object-id)
  (let loop ((n (random-integer 100)))
    (let ((id (object->serial-number (cons 1 2))))
      (if (= n 0)
   (lengthen-with-leading-zeros (number->string id 16))
   (loop (- n 1))))))
Well that was fun. If you see any serious flaws let me know. There are some Gambit-specific dependencies here but nothing too difficult to replace. The formatting's really bad, but I am off to the beach for the day so later.

Blog Archive

About Me

Portland, Oregon, United States
I'm usually writing from my favorite location on the planet, the pacific northwest of the u.s. I write for myself only and unless otherwise specified my posts here should not be taken as representing an official position of my employer. Contact me at my gee mail account, username patrickdlogan.