[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: How to remove verbosity from the data passing mechanism using alist

From: Pascal J. Bourguignon
Subject: Re: How to remove verbosity from the data passing mechanism using alist or plist ?
Date: Sun, 05 Dec 2010 23:25:13 +0100
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/23.2 (gnu/linux)

Fren Zeee <address@hidden> writes:

> [Q] Are there any defects in this method of passing struct and what
> improvements are possible ?

You didn't use a structure, you used lists.

>     Specifically, are there ways to reduce verbosity without using cl
> or staying purely in elisp ?

There is no such thing as pure elisp, or pure CL.

For elisp, it would be hard to define a core of primitives, since emacs
lisp is rather defined by its implementation, and there are various
versions of it.

For CL you could say that the set of special operators defined by the
standard (and a few mechanisms and primitive functions) are pure CL.

In both cases, most of the language is defined by library functions and
macros.  There is no difference in status between a macro or a function
specified in the CL standard (or documented in the emacs lisp reference
manual), and the macros and functions you write yourself, or you get
from a library.

Therefore I would advise you to put (require 'cl) in your ~/.emacs and
don't try to discriminate the added features.

On the other hand, you may want to be aware of the things that are
specific to emacs, such as the buffer, windows, regexp and text
processing operators, which are not available in other lisps.

First, I remark that find-my-marker-GOLD doesn't return either an a-list
or a p-list, but a list of list of two elements.  You can of course do
       (list :GOLD-value (string-to-number (replace-regexp-in-string 
"GOLD=\\([0-9]+\\)\n" "\\1"  (match-string 0))))
       (list :GOLD-location (point)))

However, a-list and p-list are known and benefit from predefined
functional abstraction, which will lead to shorter code:

     ;; a-list:
     (acons :gold-value (treasure-marker-value gold-marker) 
            (acons :gold-location (treasure-marker-point gold-marker)

     ;; p-list:
     (list :gold-value    (treasure-marker-value gold-marker) 
           :gold-location (treasure-marker-point gold-marker))

Otherwise, the point is to define and use abstractions.  This is the
secret to concise, readable and maintainble code.

- data abstraction, with data structures. (vectors, arrays,
                    lists, structures, objects, etc).

- functional abstraction, defining functions and anonymous functions,

- syntactic abstraction, using macros to define new control structures,
                         and to abstract away boilerplate code.

- meta-linguistic abstraction, defining domain-specific languages.

In your case, using data astraction and functional abstraction would be
enough to make it much clearer.

So instead of calling functions such as string-to-number or
replace-regexp-in-string that have nothing to do with gold, we call
(treasure-marker-value gold-marker)
and (treasure-marker-location gold-marker).

Notice that we don't call gold-marker-value, because there's no point in
abstracting away things that are too specific either.  It doesn't matter
if our treasure is gold, silver or diamonds!

Of course, we also need a function to create the gold-marker: 
(find-treasure "GOLD")

(defstruct treasure-marker  ; data abstraction
  what location value)

(defun make-treasure-regexp (what)
   "Make a regexp to find a WHAT treasure."
   (format "%s=\\([0-9]+\\)\n" what))

(defun find-treasure (what)
       (goto-char (point-min)) ; my bet is that it will be faster 
                               ; to search from the start.
       (if (re-search-forward (make-treasure-regexp what) nil t)
           (make-treasure-marker :what what
                                 :location (match-beginning 0)
                                 :value (string-to-number (match-string 1)))
           (error "No %s treasure found." what)))))

So now you can write simply:

(defun find-my-gold-treasure ()
  "Starting from anywhere in the file, find my marker GOLD its value and 
  (let ((treasure (find-treasure "GOLD")))
    (make-gold-treasure :value (treasure-marker-value treasure)
                        :location (treasure-marker-location treasure))))

and later use:

(gold-treasure-value treasure)
(gold-treasure-location treasure)

Of course you have to define these functions, make-gold-treasure,
gold-treasure-value, gold-treasure-location.  This is what is called
functional abstraction.

> [Q] Is there a way to avoid lengthy calling statement like
>       (car (assoc-default :GOLD-value    GOLD )
>     inside let,
>     since the first argument of let is an alist of the form
>          ((sym1 val1) (sym2 val2))

If you had a real a-list, you wouldn't have to use car.

   (assoc-default :key (acons :key 1 '())) --> 1

The usual data types in lisp are not abstract data types, and can be
used at various level of abstraction depending on the functions you
apply to them.  You can build a list of list, and then treat them as an
a-list (using assoc or assoc-default).  But of course, you have to deal
with the differences yourself.  If you want to avoid these difficulties,
you should learn the set of functions working at the same "abstraction"
level, treating the data as the same abstract data type.

So, if you want an a-list, you will use acons, assoc, assoc-default.

If you want a p-list, you will use getf.

If you want a list, you will use endp, list, first, rest, second, third, etc.

If you want a cons cell, (eg. to build a tree, or a structure or
whatever you want), you will use null, cons, car, cdr.

In all cases, we are dealing with cons cells and the symbol nil.  But we
don't consider them as the same abstract data type, depending on the
functions with use.

> [Q] Is there a way to using plists for return from find-my-marker-GOLD
>     and utilize in the user function test-GOLD

Yes.  See above.


> [Q] As you can see, I am looking for several level of solutions so I
> can weight them.
>     The main goal is brevity, style improvement and more acceptable
> style.
>  (a) Solution that is pure elisp and does not use any defmacros ,
> defclass etc.

If you have a look at the sources of emacs, bytecode.c IIRC, you will
find the source for the emacs lisp virtual machine, with the set of
primitive emacs lisp function.  Perhaps this is what one could call
"pure elisp".

Of course, the first thing you would do if you had to write "pure
elisp", is to implement a macro system, and implement a library like cl,
so that you can write code easily and productively.  That is, given a
bare machine, the first thing you do is give you the abstraction tools
required to be able to climb the abstraction ladder.  See above.

>  (b) Solution that is clisp and un-restricted.

clisp is a specific implementation of Common Lisp.  If you mean Common
Lisp, the correct abreviation is CL.

>    (c) and within both of the above, solutions with plist and alist.

Again, I won't do your own learning, I already did it myself!

And in any case, why should we repeat here what is explained perfectly
well in tens of books and tutorials?  If you need pointers, you may have
aa look at 

> [Q] test-GOLD will actually be a function called find-GOLD-processing-
> plant

Again, what about platinum processing plants and diamond processing
plants?  Functions take parameters for a reason...

You may also have fun reading:

Download instructions:

__Pascal Bourguignon__           
A bad day in () is better than a good day in {}.

reply via email to

[Prev in Thread] Current Thread [Next in Thread]