views:

400

answers:

4

I'm used to Java code, with its long descriptive names, and with lots of temporary variables used only to give a name to some return value. This kind of code is very easy to understand even after a year break.

When using Lisp, however, I can't understand things like "what is in the 3rd position of this list" the next day, or even next hour.

This problem is fatal, because I have lots of records of several fields, and in Java you call myRecord.mass and it's clear that it's mass, while (nth my-record 2) doesn't make sense at all.

Is there any practice that makes possible to write self-documenting Lisp (namely, Clojure) code in such way that there can't be questions like "what kind of data is in this position of a list"?

Or

Is it OK to pass around lots of small (~5 items) clojure Map structures?

Conclusion

The very feature I want is StructMaps -- array-based maps with shared set of keys.

Concerning self-documentation. As was with me in the case of R it was the standard library that confused me. Most builtin functions have rather non-descriptive names, and that has two consequences:

  • You have to learn some core set of names, which isn't that small.
  • I always (even unconsciously) try to obey to standard-library's naming conventions, therefore making my own function names hard to memorize.

Thanks for your answers!

+4  A: 

In Common Lisp you would use structures (= records) and classes (= classes).

Let's define a class. The class 'body' has one slot 'mass' and one superclass.

(defclass body (physical-object)
  ((mass :type number
         :documentation "the mass of the body in kg"
         :initform 0
         :initarg :mass
         :accessor body-mass))
  (:documentation "this class represents a physical body. The body has a mass."))

Let's say, we have a body called 'earth', then we can print its mass:

(print (body-mass earth))

You can create a 'body':

(make-instance 'body :mass 5973600000000000000000000)

Is that self-documenting enough?

Rainer Joswig
No, it's overdocumented. But I think `(defclass body () ((mass :type number :accessor body-mass)))` is rather good.
AnSGri
+3  A: 

I see this a lot. I guess the idea is that, because lists are so easy to create, people use them for everything, even to the exclusion of classes.

(Aside: you just don't see people writing Java x = new object[] {111,"Earth",300.145}; and then having trouble remembering which element represents what! :-)

That said, using plain lists is a very easy way to experiment. If I find myself with a list of more than about 2 things for more than about 2 seconds, I write some accessors:

(defun planet-name (p) (nth p 1))

See also Norvig's PAIP: "Whenever you develop a complex data structure, develop a corresponding consistency checker".

Ken
+9  A: 

On records:

Is it OK to pass around lots of small (~5 items) clojure Map structures?

Absolutely! Small maps are implemented as array maps internally and are really super fast.

In fact, small maps are precisely the Clojure equivalent of records. If you're going to have lots of maps sharing several keys, you may want to consider structs -- see docs on defstruct, struct, create-struct (i.e. '(doc defstruct)` etc. at the REPL).

If you're getting the latest Clojure sources from GitHub, you may want to investigate deftype instead. In that case you'll be interested in defprotocol and perhaps also reify as well. Here's the Clojure wiki page on deftype and reify with links to further wiki pages, including one for protocols.

Either way, you'll be able to write (:mass my-record) for the equivalent of myRecord.mass.

On self-documenting functional code:

I'm used to Java code, with its long descriptive names, and with lots of temporary variables used only to give a name to some return value.

There's nothing to stop you from using descriptive names for your functions, and if that means making them long, that's no problem either. In fact, some of the Common Lisp's built-in functions and macros carry somewhat notoriously long names (multiple-value-bind comes to my mind as an example).

As for the temporary variables used to give a name to a return value... You well-named functions in self-documenting expressions instead:

(euclidean-distance [x1 y1] [x2 y2])
Michał Marczyk
For those able to use deftype, it's preferred over structs in all cases.
Rayne
+1  A: 

In terms of generally writing readable Functional Code. Using Functional Decomposition can really make the code a lot easier to learn. For example:

(defn connect-and-process []
  (with-connection [db (opendb)]
    (map #(store (serialize (format-item %)) db) (get-items db))))

could be:

(defn format-for-db [item]
   (serialize (format-item item)))

(defn store-item [db item]
   (store item db)

(defn store-items [db items]
   (map #(store-item db %) items))

(defn connect-and-process []
  (with-connection [db (opendb)]
      (store-items db (get-items db))))

For functions that take a map and process it doing destructuing in the argument list can help a lot also:

(defn do-stuff [[person place action]]
  (f place (action person)))

could be more clear than:

(defn do-stuff [activity]
   (f (nth activity 2) ((nth activity 3) (nth activity 1))))

destructuing also works in let:

(let [[person place action] activity]
    (f place (action person)))

check out: http://clojure.org/special_forms

in genral binding names to things and composing small single purpose functions can really reduce the code-recovery learning curve. ps: i'm not infront of a repl so please feel free to edit my bugs :)

Arthur Ulfeldt