newLISP in context
Have you ever had one of those moments where you suddenly just get it? It just happened to me with contexts in newLisp. Allow me to share my epiphany.
What are contexts?
newLisp has dynamic namespaces. Dynamic namespaces get an undeserved bad rap from a lot of people. I have never had a problem with them. In fact, I think they are preferable for an interpreted language.
newLisp, however, is not an object-oriented language. I also don’t have a problem with this (see this article). It does mean, however, that modularization becomes a problem. Python’s modules are, in my mind, an extremely elegant solution. They often completely eliminate the need for classes because they scope a series of functions and variables together in the same namespace. In newLisp, this is solved with contexts, which are self-contained scopes.
Contexts as prototypes
Contexts are children of the ‘MAIN context. They can access each other using context:foo syntax. They are not objects or classes, although they can be hacked to act as prototypes. This last fact led me to try and treat them as prototypes. However, if one prototype has an object in it that must be prototyped from another context, we run up against the nested contexts wall.
An example of this is data modeling. I want to represent a database table. Each table has fields, and each field can be prototyped from a field context. Each table itself is a contextual prototype as well. If I create a new model using the table prototype, I now cannot access the internals of the fields in the model – (model:field:foo)
does not work.
Now, I’m not a huge proponent of OO programming, but hierachical objects are a pretty apt solution for data modeling. This is not the lisp way, though.
Contexts in context
The lispy solution for data modeling is, obviously, the list. To model my table, I made a list. The type of list that I used, in fact, was a table. I suppose it should have been obvious :).
Now, I still want to fence this list off so that it can be safely extended as well as passed as an argument to other functions (such as a function that writes sql customized for the table).
So, I defined ‘tablename:model – a symbol pointing to my list inside of the context, ‘tablename. I can then pass tablename as an argument to other functions. I can also add functions to the model for table-level functionality.
If I want, I can create a generic ‘model context to prototype my other tables from which has certain functionality already in it.
Contexts as function inputs
So, let’s say we now have an address model:
(context 'addressdb) (set 'model '("address" (relationship (type "many-to-one") (table "company") (local-key "company_id") (foreign-key "id")) (fields (("id" (type "int(11)") (key true) (uniq true) (auto true)) ("name" (type "varchar(255)") (uniq true)) ("email" (type "varchar(255)") (uniq true) (default "")) ("company" (type "int(11)")))))) (context 'MAIN)
Let’s create a simple function that returns the names of all fields in the table. It will need to access the fields using the context passed.
(define (list-fields ctx) (map first (lookup 'ctx:fields ctx:model))) (list-fields addressdb) ; => ("id" "name" "email" "company")
Let’s see how this works. The innermost expression is (lookup ‘ctx:fields ctx:model). This looks up the symbol fields (from the passed context) in the list model (from the passed context). Remember that lookup requires a symbol and therefore must be quoted. Without the quote, we are passing the value to which ‘ctx:fields points to lookup.
This leaves us with the list to which fields points inside of our model. This is another table, with strings pointing to tables of settings. We map first to the list to make a new list containing the first element of each field table.
Conclusions
Coming from an imperative programming background is one of the biggest hurdles to writing lispy code. Object oriented programming is an imperative solution. It provides other languages with organization mechanisms that lisp does not need; it has its own solutions inherent in its structure and syntax.
Once we throw our imperative baggage out, newLisp’s context are an elegant, lispy solution to prototyping and segmentation of code.