Getting it
Posted on Mon 08 June 2009 by alex in misc
Apparently according to href="http://c2.com/cgi/wiki?LispUsersAreArrogant">some learning
Lisp is a path to programming enlightenment. Although I'm not in that
school (elisp is about a better editor for me, not a way of life) I
did have a slight light-bulb illuminating moment today.
One of the jobs I have at the moment is maintaining some href="http://en.wikipedia.org/wiki/Simple_Network_Management_Protocol">SNMP
agent software. It's written in C with a hand hacked GTK GUI and very
sparsely documented. One of the tasks has been converting a bunch of
the code to abstract some of the SNMP tables into proper structures
which is a massive improvement on the dense mixture of sparsely commented OID
parsing/GUI update code. However I caught myself Copy & Pasting a lot of the
boiler plate code because most of it is identical apart from the
actual object parsing which looks like:
if (snmp_oid_compare(oid, oid_entry_A)) { obj->valueA = oid->val->integer; } else if (snmp_oid_compare(oid, oid_entry_B)) { obj->stringB = g_memdup(oid->val->string) }
Clearly something has gone wrong when you find yourself copying and
pasting code. Really it should be possible to describe the mapping in
a data structure and have one common set of code for iterating a
table. I started wondering how such a structure would work:
myMagicMappingTable[]= { {oid_a, INT, offset(&magicObj->objA)}, {oid_b, STRING, offset(&magicObj->objB)}, }
Well that would work OK, except the munging code isn't quite all that
boiler plate. The existing code isn't all straight mapping of SNMP
types to the C structure. Also using offsets into anonymous structures
is the sort of old-school C trick that one should try to avoid in
maintainable code.
As I thought on I considered a formulation involving C function
pointers in the table. This would have involved a lot of typing to
declare each function snippet before some shady casting tricks could
be pulled. "What I really need", I thought, "is some way to add
anonymous functions to my data structure". That's when the light bulb
went off and I recalled the meaning of href="http://en.wikipedia.org/wiki/First-class_function">first class
functions and the world made a little more sense.
So I can't recode this application into Lisp (or even elisp) but as an
exercise I coded up an approach to the problem in emacs to see if
things are making more sense. I ended up with:
(setq snmp-results (list (list ".1.2.3.4.5" "value A") (list ".1.2.3.4.6" "value B") (list ".1.2.3.4.7" "value C") (list ".1.2.3.4.8" "value D"))) (setq magic-object (make-hash-table :test 'equal)) (setq magic-map '( (".1.2.3.4.5" . (lambda (val obj) (puthash "thingA" val obj))) (".1.2.3.4.6" . (lambda (val obj) (message "skipping thing B"))) (".1.2.3.4.7" . (lambda (val obj) (puthash "thingC" (format "value is %s" val) obj))))) (let ((results snmp-results)) (while results (let ((pdu (pop results)) (magic)) (setq magic (assoc (car pdu) magic-map)) (if magic (funcall (cdr magic) (cdr pdu) magic-object) (message "Couldn't map:%s" pdu))))) (maphash (lambda (k v) (message "k:%s v:%s" k v)) magic-object) => k:thingA v:(value A) k:thingC v:value is (value C) nil
And that code makes sense to me. I wonder if that means I'm starting to "get lisp"?