Here is the simple example;
;; The definition is from RFC 4250-4254 ;; atom datum (define-simple-datum-define define-ssh-type read-message write-message) (define-ssh-type <name-list> (<ssh-type>) names '() (lambda (in) (let* ((len (get-unpack in "!L")) (names (get-bytevector-n in len))) (string-split (utf8->string names) #/,/))) (lambda (out names) (let ((names (string->utf8 (string-join names ",")))) (put-bytevector out (pack "!L" (bytevector-length names))) (put-bytevector out names))) :parent-metaclass <ssh-type-meta>) ;; composite data (define-composite-data-define define-ssh-message read-message write-message) (define-ssh-message <ssh-msg-keyinit> (<ssh-message>) ((type :byte +ssh-msg-kexinit+) (cookie (:byte 16)) ;; array of byte (kex-algorithms <name-list>) (server-host-key-algorithms <name-list>) (encryption-algorithms-client-to-server <name-list>) (encryption-algorithms-server-to-client <name-list>) (mac-algorithms-client-to-server <name-list>) (mac-algorithms-server-to-client <name-list>) (compression-algorithms-client-to-server <name-list> (name-list "none")) (compression-algorithms-server-to-client <name-list> (name-list "none")) (language-client-to-server <name-list> (name-list)) (language-server-to-client <name-list> (name-list)) (first-kex-packat-follows :boolean #f) (reserved :uint32 0)))So the idea of the library is that structured data are either simple datum or composite of simple datum. Thus if we define how to read/write the simple datum, then composite data's read/write are already decided. This might not be always true but as far as I know most of the case.
BTW, I think the naming of the macro is ugly so if you have a better suggestion it's very welcome :)