Log in

quicknet: a proof of concept file

I'm trying to make a single-file, cross-implementation program that involves some networking. I only need a handful of relatively high-level functions: connect to a port on a host, write octets, read available octets, and disconnect.

Most similar attempts I've seen involve functions with a bunch of intermixed #+foo and #-bar, and I find that kind of ugly. It's a little less ugly to have something approximately like #+foo (load "foo-defs.lisp"), but that defeats my single-file goal.

I decided to give it a try with CLOS, using an object to represent the current implementation and defining methods that specialize on the implementation class. For example, here's part of the file to support LispWorks:

;;; LispWorks

(define-implementation-package :lispworks #:qn.lw
   (require "comm"))
  (:import-from #:comm
  (:import-from #:system
  (:export #:open-tcp-stream

(defclass qn.lw:lisp (lisp connections-are-streams) ())

(defmethod %open-connection (host port (lisp qn.lw:lisp))
  (qn.lw:open-tcp-stream host port
                          :direction :io
                          :read-timeout 0
                          :element-type 'octet))

(defmethod %read-octets :before (buffer connection (lisp qn.lw:lisp))
  (declare (ignore buffer))
  (qn.lw:wait-for-input-streams (list connection)))

(setf *lisp* (make-instance 'qn.lw:lisp))

I split a subset of the program into its own file with just the networking stuff: quicknet.lisp. It supports SBCL, CLISP, LispWorks, Allegro Common Lisp, ECL, and Clozure CL.

I'd love to get some feedback. What do you think of the approach? Is there something I should do differently or better?




Class factory

Congrats, you seem to have reinvented the Class Factory pattern ;-)

Seriously, it looks like a good general technique, though I can't decide for myself whether it's too general for the particular application.
Looks like a sane approach to me.


A case for ContextL?

Hmmm, could you maybe remove the implementation parameter and instead use ContextL layers for representing the implementation?

Then you could say something like:

(define-layered-method open-connection :in lispworks (host port)


Re: A case for ContextL?

ContextL is pretty cool, and i think it is a good fit for a "normal" application. I'm not-so-artificially constraining myself to implementations that will fit reasonably in a single file.
In my opinion, you did not get advantage over #+. Why is your solution better? In both cases we say: "this piece of code is for LispWorks and this piece is for CLISP".

Your solution involves more code: wrappers that pass global *lisp* to %-functions, implementation classes and their instances, define-implementation-package, etc. All this requires understanding and concentration efforts. For example, my question was: how do you get implementation-specific methods compiled, if the code refers nonstandard packages, that is absent in other implementations. It took some time to understand how you reexport the symbols from created package (btw, it was educative for me). Another disadvantage is that methods for all the implementations other than current are present in runtime too and consume some (maybe minor) resources.

There is something about how you reused code for implementations where sockets are streams, but I believe with #+ it is not more difficult.

BTW, why don't you like #+? If it is just too prickly for eyes, maybe changing readtable to have, say, @+ instead, will make the code more sleek and round and therefore easier to read? :))

- Anton


Nice example, but have a care

Your experiment makes a good case for CLOS and method specialization techniques in general. Keep in mind that some CLOS implementations give you a special bonus: extra cost plus at runtime for method selection. You would expect some cost over a defun, but sometimes it's more than you'd expect. So, things that get called a lot, like %read- and %write-, for example, method selection may be cost-prohibitive. (time) will tell...

Re: Nice example, but have a care

In my case, network i/o dominates everything.