View all entries in Article

C++ Coder’s Newbie Guide to Lisp-style OO

People tend to think of functional programming, recursion and lists when they hear the word Lisp. However, as I'm about to show you, the OO capabilities of Lisp in general and the CLOS -- Common Lisp Object System -- in particular, are comparable to--even surpassing!--the model used in C++. Note that the CLOS isn't something separate you have to download, or a part of the language that people don't use -- it's integrated in the language, people use it and fully documented in The HyperSpec. Most importantly, it doesn't feel bolted-on, as some object-oriented extensions in other languages do.

First, in order to reach common grounds here, I'm going to present a dumbed-down version of C++ and build on that, followed by a presentation of the CLOS and how its features relate to what was described in the section on C++. As I'm still learning about this myself, I will not show all features of the CLOS, just the basics that I feel somewhat comfortable talking about. Finally, the text is wrapped up with a toy OO implementation in Lisp, spanning just under ten lines of code.

(For more information about Lisp in general and the CLOS in particular, consult Peter Seibel's excellent Common Lisp tutorial (Practical Common Lisp, Apress, 2005), especially Chapters 16 and 17.)

What is C++-style OO About?

Encapsulation and information hiding are two OO concepts most programmers know of, and probably the main points. Yes, inheritance is, too, highly powerful, and I will address it later in the text. But first things first.

Just so we're talking about the same things, I'm going to reiterate the concepts. Encapsulation is the method of packaging data and code operating on the data together in one place, the class. Information hiding, which you might argue not being very important for being object-oriented, is still something C++ programmers see as one of the main concepts: if you think of the class as a black box, you can't touch what's inside (the data inside the class), other than by pulling levers and pushing buttons on the box (the code in the class operating on the data).

Encapsulation & Information Hiding

As an example of information hiding and encapsulation, consider a car. You're not particularly interested in all the components (i.e., the data) that make up the car, but rather what you can do with the car (i.e., the code operating on the data). Let's say you didn't care much about encapsulation, and you might end up with something like this for the task "drive the car":

int angle[CAR_COUNT];
bool brake_pedal[CAR_COUNT];
bool gas_pedal[CAR_COUNT];

void drive_car(int car) {
    ...
}

// user code
angle[0] = 45;
brake_pedal[0] = false;
gas_pedal[0] = true;
drive_car(0);

This is a slightly contrived example, as you would probably not write code like this should you decide not to package it up in a class proper. However, it's just an illustration of exposing the inner workings of an object, as opposed to encapsulating it. Here's how you could write it using classes in C++:

class Car {
public:
    // levers and buttons
    void faster(car_t *);
    void slower(car_t *);
    void stop(car_t *);
    void turn(car_t *, int angle);
private:
    // nuts and bolts
    wheel_t wheel;
    bool brake;
    bool gas;
};

Car car;
car.faster();
car.turn(45);

and so on. This is the better interface, even though the previous one was exaggerated in its stupidity. Anyhow, the point of the second example is to show that by wrapping your data in a (opaque) black box, you can perform operations on the box without knowing how the box works inside. The packaging of the data and the code in a box is called encapsulation, and the part about displaying only levers and buttons--not letting the user tamper with the nuts and bolts--is information hiding. Now that we've got that part cleared up, let's discuss what it actually means, in terms of programming.

Simply put, you have an object (a car), and operations you can do to it, without knowing (in fact, caring!) how the object gets them done. In the C world, you talk about a structure containing variables together with functions doing things to the variables in that structure. You can easily implement the same thing in C, albeit with a slightly uglier syntax.

More OOP in C++

There's more to C++ than what I've just described. You also get interesting things like (multiple) inheritance and (virtual) methods in classes. Let's find out what it looks like!

Given the canonical example of different fruits, which all have different colors and can be peeled (some more successfully than others). Instead of defining the color and the peeling operation individually, for each type of fruit, you'd make a base class with that information and, if necessary, let the new type of fruit override the default behavior. This way, you can pass along different kinds of fruit to functions accepting a Fruit:

class Fruit {
public:
    virtual void peel();
    virtual void eat();
    color_t color;
private:
};

class Apple : public Fruit {
public:
    void peel() {
        Fruit::peel(); // default peeling
        // perform peeling specific for an apple
        ...
    }
};

class Banana : public Fruit {
    ...
};

This way, you can pass an Apple to anything that accepts a Fruit:

void i_like_peeling_fruit(Fruit &fruit) {
    fruit.peel();
}

...and there was much rejoicing. Other information that could prove useful is to know the dimensions of an Apple:

class Thing {
public:
    virtual float height();
    virtual float width();
    virtual float weight();
};

class Apple : public Fruit, Thing {
    ...
};

We now have access to three more method related to physical properties of the fruit. Notice how all these methods are bound to the class, and in fact, what happens is that there's an implicit this variable passed as the first parameter to each function -- here's what it looks like, underneath the sugar coating:

float height(Thing *thing)

Except that, of course, if you wanted to move the method outside of the class, it would have to be declared friend of the class. One of the reasons for putting the function inside the class is that you get that first parameter passed automatically, and not having to write friend declarations.

OK, let's say you want to extend the peel() method with a new parameter, the Peeler:

class Fruit {
public:
    virtual void peel(Peeler &peeler);
    ...
};

Now, does this really belong to Fruit, or should a Peeler accept a Fruit? We could always move peel() outside of both classes, like this:

class Peeler;
class Fruit {
public:
    friend void peel(Fruit &, Peeler &);
};

class Peeler {
public:
   friend void peel(Fruit &, Peeler &);
};

void peel(Fruit &fruit, Peeler &peeler) {
    ...
}

There'll be a bit of work to update the parameter list of peel() in all classes each time, but now you're safe if you add a class SteelPeeler in the future and don't feel comfortable with Apple being the owner of the method.

However, it's not quite that easy...:

class Peeler;
class Fruit {
public:
    friend void peel(Fruit &, Peeler &);
};
class Peeler {
public:
    friend void peel(Fruit &, Peeler &);
};

class Apple : public Fruit {
public:
    friend void peel(Apple &, Peeler &);
};

void peel(Fruit &fruit, Peeler &peeler) {
    puts("peel(Fruit, Peeler)");
}
void peel(Apple &fruit, Peeler &peeler) {
    puts("peel(Apple, Peeler)");
}

We've defined a bunch of different signatures for the peel() method, and if you send in objects of various type, you expect them to get dispatched properly to the correct method:

int main() {
    Fruit *fruit = new Fruit;
    Peeler *peeler = new Peeler;
    peel(*fruit, *peeler);

    Apple *apple = new Apple;
    peel(*apple, *peeler);

    fruit = apple;
    peel(*fruit, *peeler);
}

=> peel(Fruit, Peeler)
   peel(Apple, Peeler);
   peel(Fruit, Peeler); <-- hey! what happened to my Apple?

Well, it's half-way there.

Later in this article, I'll show you how Lisp deals with this.

Introducing the CLOS

I'd rather not write that the CLOS (from now on I'll just say Lisp, by which I mean Common Lisp and its standard), can do the same things as C++ (only more!), because that will instantaneously cause people to ignore the "only more!" parts, as C++ is generally considered good enough. Well, just bear with me for just a few more paragraphs, and I'll show you (a limited subset of) what Lisp has to offer (the reason for not showing you all advanced techniques such as the Metaobject Protocol is because I haven't yet learned them), and you can get quite far by just understanding the basics.

Classes

The first thing you should know about Lisp is that a class defines only the slots, i.e., data related to an object, and not the methods. So you could say it's just a struct, right? No! The reason for methods not being defined in classes, is (in part) because Lisp does multiple dispatch, also known as multimethods. (I'm going to touch briefly on that later in the text.)

Here we go, define a class:

(defclass fruit ()
  ((color
     :initarg :color
     :initform :red   ; by default, all fruits are red
     :accessor color)
   (skinp
    :initarg :skinp
    :initform t       ; and, they have skin
    :accessor skinp)))

Define the class fruit, with slots color and skinp (following the Lisp convention of ending the names of predicates (booleans) with p; think of it as "bool skin"). We have the keywords :initarg, :initform and :accessor. Starting with :initarg, it tells make-instance (the more-or-less equivalent of new) which parameter designates the slot color, :initform is the default value of the slot if an initial argument (:initarg) wasn't given for make-instance, and finally the "getter/setter" :accessor. Might sound a bit complicated, perhaps, but this example will help you understand:

(let ((apple (make-instance 'fruit)
      (banana (make-instance 'fruit :color :yellow)))
  (format t "Apples are ~A and bananas are ~A, honey is sweet but not as sweet as you.~%"
            (color apple) (color banana)))
=> Apples are RED and bananas are YELLOW, honey is sweet but not as sweet as you.

I hope there's nothing unclear here: the function color comes from the :accessor slot specification in the fruit class.

Generic Functions and Methods

Recall how I said that in C++, methods of a class has an implicit first parameter, an object with the type of the class the method belongs to? E.g. void Fruit::peel() is really just another way to spell void peel(Fruit *). That's known as single dispatch by another name -- methods are specialized on the first argument. In order to use the same methods in other classes, you create a mixin or lift out the common functionality into a base class. In Lisp, methods are created in two steps, a generic function and a method defined on it -- however, methods themselves do not belong to a specific class, but its arguments can be specialized on one or more classes. Note that everything in Lisp is a class (type, really): strings, numbers, you name it, with everything inheriting from the base class T. It is also the canonical truth constant, but it is not a direct instance of the class T, but instead the class SYMBOL.

Here's the definition of two useful generic functions operating on the previously defined class:

(defgeneric peel (fruit)
  (:documentation "Peel a fruit"))
(defgeneric eat (fruit)
  (:documentation "Eat a fruit. Make sure it's peeled!"))

Lisp terminology used here is the generic function, which like the name says, is a function that can specialize its argument on any class. You cannot yet use peel and eat -- they're similar to pure virtual functions, so you need to define methods on them first. Introducing methods operating on the fruit and an example to show how they work:

(defmethod peel (fruit)
  (setf (skinp fruit) nil))

(defmethod eat (fruit)
  (if (skinp fruit)
    (format t "I don't like fruit with skin :(~%")
    (format t "Munch munch, I love ~A fruit!~%" (color fruit))))

(let ((banana (make-instance 'fruit :color :yellow)))
  (format t "Does the banana have skin? ~A~%" (skinp banana))
  (eat banana)
  (peel banana)
  (format t "Banana now peeled, so does it still have skin? ~A~%" (skinp banana))
  (eat banana))
=> Does the banana have skin? T
   I don't like fruit with skin :(
   Banana now peeled, so does it still have skin? NIL
   Munch munch, I love YELLOW fruit!

Inheritance

I like apples, in fact, I prefer apples over bananas. But I don't peel them. In C++, you would inherit from fruit and override the peel method to do something else. It's basically the same in Lisp. Let's throw in the name of the apple while we're at it:

(defclass apple (fruit)
  ((name
    :initarg :name
    :accessor name)))

(defmethod eat ((fruit apple))
  (format t "Munch munch, ~A apples are always yummie, skin or not!~%" (name fruit)))

(defmethod peel ((fruit apple))
  (format t "Don't want to peel apples!~%"))

(let ((apple (make-instance 'apple :name "Royal Gala")))
  (peel apple)
  (eat apple))
=> Don't want to peel apples!
   Munch munch, Royal Gala apples are always yummie, skin or not!

Presto! eat and peel are now specialized on apples, where you don't care much about the skin. In fact, you can specialize on a specific object in generic methods, should you want to. Like this:

(defvar *the-kings-apple* (make-instance 'apple :name "Grand Royale"))
(defmethod eat ((fruit (eql *the-kings-apple*)))
  (format t "Greetings, my lord, I hope the apple pleases you!~%"))
(eat *the-kings-apple*)
=> Greetings, my lord, I hope the apple pleases you!

A caveat regarding inheritance and specialized methods: unless you want to take full responsibility of the more general specializations of the method, i.e. (defmethod eat (fruit) ...), you should call call-next-method at the end, which makes sure they gets called, by combining them in a specific order (user-configurable). By default, they're called in roughly the same way as C++'s Base::method(argument) in a child class' method.

You can also do sillier things with eql specialization ("pattern matching", on the object level):

;; DEFGENERIC is not strictly neccessary, as most Lisps will
;; automatically create a generic function for you should it
;; not exist at method definition time.
(defgeneric fact (n)
  (:documentation "Calculate the factorial of n"))

(defmethod fact ((n (eql 0))) 1)
(defmethod fact ((n integer)) (* n (fact (1- n))))

(fact 5)
=> 120

Method Combination

Of course, if you decide you couldn't be bothered making sure the fruit is peeled before you (eat ...) it, you can always add a :before method that does this for you. (Now, as it's your own code, you could also just modify the method directly, however you can use method combinations for other's libraries, essentially forming it to your needs without actually touching the library itself!):

(defmethod eat :before (fruit)
  (unless (typep fruit 'apple) ; apples are always yummie, so no peeling!
    (peel fruit)))

(eat (make-instance 'fruit :color :yellow))
=> Munch, munch, I love YELLOW fruit!

There's also the equivalent :after method. Other uses for :before/:after is logging. There's one last that requires our attention and that's :around. It's used to wrap a piece of code, typically rebinding dynamic variables. We'll be using it to bind *standard-output*, so anything printing text on the screen will return it as a string instead:

(defmethod eat :around (fruit)
  (with-output-to-string (*standard-output*)
    (call-next-method)))

(let* ((apple (make-instance 'apple :name "Royal Gala"))
       (what-he-said (eat apple)))
  (format t "\"~A\", he proclaimed!~%" what-he-said))
=> "Munch munch, Royal Gala apples are always yummie, skin or not!", he proclaimed!

Among the other fun things you can do with :around methods is memoization (left as an exercise to the reader).

Utilities

When you're doing lots of slot juggling, it can become tedious to write (accessor instance) everywhere, especially if you're manipulating the value. Example (assuming *apple* is an instance of the class apple):

(setf (name *apple*) (format nil "~A, ~A" (name *apple*) (color *apple*)))
=> "Royal Gala, RED"

Lots of apple! Instead, use with-accessors:

(with-accessors ((a-name name)) *apple*
  (setf a-name (format nil "~A, ~A" a-name (color *apple*))))
=> "Royal Gala, RED"

Okay, we weren't doing a lot of juggling here, but you get the point. It's a convenient shortcut.

Multiple Dispatch

Remember the Fruit/Peeler dilemma we had above, where it's sometimes hard to decide which class a method should belong to? Turns out you don't have to make that decision in Lisp! Modifying the original example slightly:

;; stub classes for illustration purposes, full definitions
;; of the classes is unimportant.
(defclass fruit () nil)
(defclass apple (fruit) nil)
(defclass peeler () nil)
(defclass steel-peeler (peeler) nil)

(defgeneric peel (fruit peeler)
  (:documentation "Peel the fruit using the peeler"))

(defmethod peel (fruit peeler)                        (format t "(peel fruit peeler)~%"))
(defmethod peel ((fruit apple) peeler)                (format t "(peel apple peeler)~%"))
(defmethod peel (fruit (peeler steel-peeler))         (format t "(peel fruit steel-peeler)~%"))
(defmethod peel ((fruit apple) (peeler steel-peeler)) (format t "(peel apple steel-peeler)~%"))

Admittedly, because symbols in Lisp carry type information, this is almost cheating. However, it does do what you'd expect, unlike C++:

(let ((fruit        (make-instance 'fruit))
      (apple        (make-instance 'apple))
      (peeler       (make-instance 'peeler))
      (steel-peeler (make-instance 'steel-peeler)))
  (peel fruit peeler)
  (peel apple peeler)
  (peel apple steel-peeler)
  (peel fruit steel-peeler))
=> (peel fruit peeler)
   (peel apple peeler)
   (peel apple steel-peeler)
   (peel fruit steel-peeler)

Look, it's a quine! More importantly, however, it does the right thing.

So, one of the things we gain by multiple dispatch is not having to do an artificial split-up of the responsibilities of classes. The problem can be solved in other languages through the use of something similar to the Visitor pattern or with metaprogramming.

Summing Up

This concludes my basic introduction to the CLOS. There are quite a few topics which I haven't covered, among which:

  • Multiple inheritance
  • CLOS Metaobject Protocol

Moreover, method combination wasn't covered in depth. For more information about those topics and the CLOS, have a look at the chapters Object Reorientation: Generic Functions and Object Reorientation: Classes in Peter Seibel's Practical Common Lisp.

Just For Fun: Poor Man's OO in 9 Lines of Code and 6 Lines of Sugar

One way to look at OO is the lumping together of data, and code operating on it, in an easy-to-use package. To achieve that, you could just use anonymous symbols and hash tables, just like Smug Lisp Weenies did 30 years ago, without any of the fancy CLOS!

In 2008, that's what Lua and Perl do. So why can't we?

Disclaimer

The purpose of the section that follows is to show you how you can play with OO, completely without using the CLOS -- I'm just using standard Lisp building blocks, specifically closures and hash tables. Usually, you would use CLOS, though -- there really is little point in reinventing the square wheel!

With that clarified, let's begin. A few key concepts first!

What's a Closure?

All lexical ("local") variables available at a certain point in time is called the lexical environment, for example in a function. When execution continues past the scope of the function, those variables are no longer accessible. A closure is a piece of code that gets a "copy" of the lexical environment that, when returned, can still use the lexical environment.

To exemplify, let's pretend you can pass around functions as variables in C, through the use of the new type function. Since a few years back, mainstream languages have started to introduce this feature, so if you're already familiar with it, just skip ahead to the introduction of the hash table.

Here's a silly example of how you can create a function that adds a constant number to its input:

function create_adder(int how_much) {
    int the_adder(int number) {
        return how_much + number;
    }
    return the_adder;
}

Let's also throw in "inner functions" to our extended C. What happens here is that the_adder closes over the current lexical environment (in this case is the variable how_much), which is then returned by create_adder.

You can now use it in the following way:

function add5 = create_adder(5);
printf("Result of add5(10): %d\n", add5(10));
=> Result of add5(10): 15

Also pretend that our C also has an interactive prompt, like the one you get by starting (to use a familiar application) Python. Let's name it The REPL (which is the proper Lisp term for the interactive prompt), short for Read (parse the input line), Eval (execute the expression), Print (print the results of the expression, or computation) and finally Loop (give the user the chance to input more expressions).

The above example would look like this in Lisp:

(defun make-adder (how-much)
  (lambda (n) (+ n how-much)))
(setq add5 (make-adder 5))
(format t "Result of (add5 10): ~a~%" (funcall add5 10))
=> Result of add5(10): 15

(note the Lisp convention of make-foo)

Another way of doing closures, which looks more like the code coming up next, is by using a locally defined name and closing over that instead of just the parameter:

(defun make-double-and-adder (how-much)
  (let ((variable-bound-over-adder (* 2 how-much)))
    (lambda (n) (+ n variable-bound-over-adder))))
(setq add-to-double-of-5 (make-double-and-adder 5))
(format t "Result of (add-to-double-of-5 10): ~a~%" (funcall add-to-double-of-5 10))
=> Result of (add-to-double-of-5 10): 20

Introducing the Hash Table

Now that we've introduced closures over function parameters and let-bound variables and having been inspired by Closures + Hash Tables = As Much OOP as You'll Ever Need, let's make a leap by introducing hash tables for slot access and see what happens:

(defun make-obj ()
  (let ((h (make-hash-table)))
    (lambda (command field &optional value &rest args)
      (case command
        (get (gethash field h))
        (set (setf (gethash field h) value))
        (run (apply (gethash field h) args))))))

Fantastic, isn't it! No? Just Lots of Irritating Superfluous Parentheses? Alright, let's break it down:

(lambda (command field &optional value &rest args)

This creates an anonymous function that takes two parameters, command and field, together with the optional parameter value. Say you named it perform, it would be used like this:

(perform 'set :name "Lisper")
(perform 'get :name)
=> "Lisper"

Because it closes over the hash table h, it will be available in the lambda. Moreover, it will continue to be available "outside" of make-obj, i.e. by the code using the newly created object. The last part we haven't covered is the RUN operation. In order to do that, we need to set a slot's value to a function. Let's do that:

(defvar *obj* (make-object))
(funcall *obj* 'set :say
  (lambda (name) (format t "Hello, ~A!~%" name)))
(funcall *obj* 'run :say "Lisper")
=> Hello, Lisper!

If you wonder where the funcall came from, it's there because we're returning an anonymous function in make-obj. Still a bit cumbersome to use, though. Here's where Lisp shines, as you can freely define syntax to make your life easier. So let's add some sugar:

(defun ~ (obj field)
  "Get a field in the object"
  (funcall obj 'get field))
(defun ~= (obj field value)
  "Set a field in the object"
  (funcall obj 'set field value))
(defun ~! (obj field &rest arguments)
  "Call the function specified by the field with a list of arguments."
  (funcall obj 'run field arguments))

With this, the Hello snippet above can be rewritten as follows:

(defvar *obj* (make-object))
(~= *obj* :say (lambda (name) (format t "Hello, ~A!~%" name)))
(~! *obj* :say "Lisper")
=> Hello, Lisper!

Quite nice, wouldn't you say?

Getting Hold of Self

Now, what if we wanted to access a slot in the object? We'd need to pass along the object to function calls (this, self, etc.) That would have to be done at the point of the function call, in make-object:

(defun make-object ()
  (let ((h (make-hash-table)))
    (labels
      ((dispatcher (command field &optional value &rest args)
        (case command
          (get (gethash field h))
          (set (setf (gethash field h) value))
          (run (apply (gethash field h) (append (cons #'dispatcher value) args))))))
    #'dispatcher)))

This is just slightly more complicated. What we did was to name the dispatcher properly, and prepending it to the argument list the functions receive. This is what our third iteration of the Hello program looks like (I use self, but you are of course free to use whatever name you wish, like this):

(defvar *obj* (make-object))
(~= *obj* :name "Lisper")
(~= *obj* :say
  (lambda (self) (format t "Hello, ~A!~%" (~ self name))))
(~! *obj* :say)
=> Hello, Lisper!

As a final step, let's make it a bit convenient to use by unifying GET and SET, like the dot-notation of C/C++, in the method ~:

(defun ~ (obj field &optional (value nil value-supplied-p))
  "Get or set a field in the object"
  (if value-supplied-p
    (funcall obj 'set field value)
    (funcall obj 'get field)))

Boiling It All Down

So, the full toy implementation, with an example, looks like this:

;; produces an object--a closure
(defun make-object ()
  (let ((h (make-hash-table)))
    (labels
      ((dispatcher (command field &optional value &rest args)
        (case command
          (get (gethash field h))
          (set (setf (gethash field h) value))
          (run (apply (gethash field h) (append (cons #'dispatcher value) args))))))
    #'dispatcher)))

;; sugar
(defun ~ (obj field &optional (value nil value-supplied-p))
  "Get or set a field in the object"
  (if value-supplied-p
    (funcall obj 'set field value)
    (funcall obj 'get field)))

(defun ~! (obj field &rest arguments)
  "Call the function specified by the field with a list of arguments."
  (funcall obj 'run field arguments))

;; example usage
(defvar *obj* (make-object))

(~ *obj* :name "Lisper") ; set the name
(~ *obj* :say (lambda (self) (format t "Hello, ~A!~%" (~ self :name))))
(~ *obj* :tell (lambda (self who what) (format t "Hey ~A, ~A asked me to tell you about ~A~%"
                                                 who (~ self :name) what)))
(~ *obj* :name) ; get the name!
=> "Lisper"

(~! *obj* :say) ; function call
=> Hello, Lisper!

(~! *obj* :tell "C++ Hacker" "Practical Common Lisp - you should read it!") ; function call
=> Hey C++ Hacker, Lisper asked me to tell you about Practical Common Lisp - you should read it!

Not bad at all!

In conclusion, you can come a long way with pure Lisp, which goes to show the capabilities of Lisp as a language toolbox. Of course, for real applications, you should exploit the full power of CLOS.

Happy hacking!

CHANGELOG

2008-04-01:make-obj should be make-object. Clarified the meaning of T. Thanks, Giorgos!

Responses

You can follow any responses to this entry through the RSS 2.0 feed.

  1. Giorgos said on April 2nd, 2008 at 08:23 (link)

    Great article! Exactly the kind of thing a former C++ user and Lisp newbie like me needed. Thank you for taking the time to write this!

    I think I have one or two corrections, though.
    According to note 4 in chapter 16 of Practical Common Lisp:

    “T the constant value and T the class have no particular relationship except they happen to have the same name. T the value is a direct instance of the class SYMBOL and only indirectly an instance of T the class.”

    Also, if I understand correctly, you should call (defvar *obj* (make-object)) instead of (defvar *obj* (make-obj)) in the “Poor Man’s OO” section.

  2. Mikael Jansson said on April 2nd, 2008 at 08:32 (link)

    Giorgos: I’m glad you found it useful!

    Thanks for comments and corrections, I’ll update the article with the new info.

  3. Brendan Baldwin Dot Com » Blog Archive » links for 2008-04-02 said on April 2nd, 2008 at 16:33 (link)

    [...] C++ Coder’s Newbie Guide to Lisp-style OO | Mikael Jansson Reference article on Lisp coding style from a traditional OO-style language perspective. (tags: lisp style programming) [...]

  4. Kevin’s Link Blog » CLOSer To Lisp said on April 7th, 2008 at 11:45 (link)

    [...] Newbie Guide To Lisp OO [...]

  5. Art said on April 17th, 2009 at 01:27 (link)

    In the code parentheses are placed odd:

    (let ((apple (make-instance 'fruit)
          (banana (make-instance 'fruit :color :yellow))))
      (format t "Apples are ~A and bananas are ~A, honey is sweet but not as sweet as you.~%"
                (color apple) (color banana)))

    Should be:

    (let ((apple (make-instance 'fruit))
          (banana (make-instance 'fruit :color :yellow)))
    .…

    Thanks! Fixed.

If this is the first time you comment on my site, your post might get held up in the moderation queue. Don't panic, I'll approve it as soon as possible!

Code (i.e. literal) blocks should be wrapped inside <PRE>.

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Close
Powered by ShareThis