

Getters/Setters/Fuxors - nailer
http://tomayko.com/writings/getters-setters-fuxors

======
mahmud
Fifteen lines for fifteen, functionally identical, and no where have I
implemented a getter or a setter :-)

    
    
      ;; define the class
      (defclass contact ()
        ((first-name   :initarg   :first-name   :accessor contact-first-name)
         (last-name    :initarg   :last-name    :accessor contact-last-name)
         (display-name :initarg   :display-name :accessor contact-display-name)
         (email        :initarg   :email        :accessor contact-email)))
    
      ;; printing is built into lisp, you just customize its behavior
      (defmethod print-object ((c contact) (stream t))
        (print-unreadable-object (c stream)
          (format stream "~a<~a>" (contact-display-name c) (contact-email c))))
    
      ;; implement some logic to validate the the slot values
      (defmethod initialize-instance :after ((c contact) &rest initargs &key &allow-other-keys)
        (when (slot-boundp c 'email)
          (unless (valid-email-p (contact-email c))
            (error "~a should be a valid email" (contact-email c)))))
    
      (defun valid-email-p (email)
        (check-type email string)
        (find #\@ email))

~~~
nailer
Your code is the same size and still runs the method when when the state is
changed - which is cool. But what's the advantage?

~~~
mahmud
The advantage:

1) I can generate accessors, setters or getters automatically with just a
keyword; :accessor, :reader, :writer.

2) DEFCLASS can be wrapped to provide further functionality with no change to
the source code. For example, if I had wrapped defclass with a macro named
defview, I could have the same exact code, but inherit from a serialized class
or use a serialized metaclass. Both Postmodern and CLSQL ORMs will let me use
a DB-CLASS or similar, and the class will map to a database table. All
instantiated objects will be persisted to this table as records. Should I have
gone the NoSQL route, I could persist to Redis, MongoDB, Berkeley DB or
TokyoCabinet, with the _exact_ same code.

3) Print object is a generic function of two arguments, class and stream. I
can have as many of it, with the second argument specialized to various
classes; TEXT-AREA, DIALOG-BOX, HTTP-STREAM, SHELL-BUFFER, FILE or whatever.
The rendering will be different for each; my object would be rendered as a
text in a buffer for TEXT-AREA, as editable GUI elements for DIALOG-BOX, as an
HTML form for HTTP-STREAM, as plain text for the unix shell in SHELL-BUFFER,
as a structured CSV/XML for FILE, etc. It's all up to me, all with no change
in the code. To port from CLI to the web I would just have to write a series
of "backends" and the logic stays the same.

4) INITIALIZE-INSTANCE is a standard generic function built into Lisp. The
generic function dispatch is a bag of tricks all on its own. Right now I only
wrapped the initialization protocol once, actually it's an :after method,
which doesn't interrupt the initialization sequence but only does some post
processing. There are also :before, and :around methods, which take place
before the applicable method is called or instead of it, respectively. I could
have as many of those as I want, with the most specialized ones being called
first. The generic dispatch has further configurations; I don't need to call
the functions in this order, I could use a host of dispatch methods modeled
after Lisp operators: PROGN dispatch would call all the applicable methods in
sequence. OR would return the result of the first non-NIL method. AND would
return a value of all applicable methods that don't return a non-NIL value.
Others concatenate the result of applicable methods. CLOS is on a class of
it's own.

5) I have used CHECK-TYPE and ERROR for error checking, both of which throw
you in the debugger with several restarts as options. I could have used SIGNAL
and started passing around some high-level conditions. Advanced control
semantics the likes of which this side of call/cc has never seen.

6) I wrote a VALID-EMAIL-P predicate to validate email input. I could have
written a generic function, VALID-INPUT-P which dispatches on the type of its
arguments, and used regular expressions to validate my input. In my project, I
don't even do input validation. The rules for valid input come from my users
who know the application domain better than I. This is what makes a typical
application more of a "Business Rules Engine". My boss has a table of all the
types of data used in the system, including stuff I don't care about;
EXPIRATION-DATE, for example, is a date input type with further constraints
coming from several parts of my application, including billing. I could never
foresee input validation like this, it would be a nightmare to hardcode this
into the app, so I exported a web form where they can input business rules in
plain English which gets compiled in to Lisp code (actually, so far it's a
human compiler, moi, but I hope to develop this further into a DSL; I cheat
and write regular expressions, along with some methods that query the various
parts of the sytem, the exact regexes are used by the javascript client
validation as well.)

I can look at a page of my code, and the are becomes a volume! I can see the
screen of my laptop translate further back in to the horizon, on a negative
Z-axis, giving that snippet of code of mine far more depth and weight than it
would otherwise have in any other shallow language. It's hard, taxing work,
but once you tame it, the possibilities are endless.

~~~
nailer
Thanks for that, it was a real eye-opener.

~~~
mahmud
No worries :-)

I tried to reach you on freenode but I guess you were away.

------
nailer
A nice way to validate object state changes when you need to without having to
make getters/setters for when you don't.

