From: bradb
Subject: Code critique request.
Date: 
Message-ID: <1131429665.516727.220540@g44g2000cwa.googlegroups.com>
Hello all - I am brand new to Lisp, and as a learning experience I've
been writing a simple editor.  It is basically Vim-like, uses Ltk for
the front end and is very incomplete.  With Ltk installed, you should
be able to (load "ltk-fe.lisp") and run (main).
I would really appreciate if more experienced Lispers (ie, anyone)
could take a gander at the code.  I am especially interested in
 - where I am being un-lispy
 - where I am using or abusing Lisp idioms
 - obviously inefficient code (particularly when I could have done the
same thing, without impacting on flexibility or code complexity)

But all (constructive) comments are more than welcome.
The file is here http://isiscarter.com/vim-in-lisp.tgz

Many thanks
Brad

From: Zach Beane
Subject: Re: Code critique request.
Date: 
Message-ID: <m3zmof41nn.fsf@unnamed.xach.com>
"bradb" <··············@gmail.com> writes:

> Hello all - I am brand new to Lisp, and as a learning experience I've
> been writing a simple editor.  It is basically Vim-like, uses Ltk for
> the front end and is very incomplete.  With Ltk installed, you should
> be able to (load "ltk-fe.lisp") and run (main).
> I would really appreciate if more experienced Lispers (ie, anyone)
> could take a gander at the code.  I am especially interested in
>  - where I am being un-lispy
>  - where I am using or abusing Lisp idioms
>  - obviously inefficient code (particularly when I could have done the
> same thing, without impacting on flexibility or code complexity)
> 
> But all (constructive) comments are more than welcome.
> The file is here http://isiscarter.com/vim-in-lisp.tgz

Some random thoughts:

   - work within packages

   - when there is more than one file in a project, create some way to
     easily load the files in the right order (e.g. an ASDF system
     definition)

   - "(if test (progn foo bar baz)) " is better written 
     "(when test foo bar baz)"

   - Multi-line IF indentation should be indented like this:

     (if test
         then-part
         otherwise-part)

   - don't put closing parentheses on their own lines

   - indent consistently (some of your defuns are indented one space,
     others two)

   - COND clauses can have multiple forms; no need to use progn

   - CASE keys are not evaluated.

       (case key-sym
         ('SHIFT_L         (setf *shift-down* val))
         ('SHIFT_R         (setf *shift-down* val))
         ...)

    No need to quote SHIFT_L, SHIFT_R, etc.

You can get a lot of mileage out of using Emacs and setting
lisp-indent-function to common-lisp-indent-function. Using slime will
also get you correct indentation for user-defined macros that take a
&body argument.

Zach
From: bradb
Subject: Re: Code critique request.
Date: 
Message-ID: <1131468463.213624.137530@z14g2000cwz.googlegroups.com>
Thanks for taking the time to look over the code, the feedback is good.
I have a question about the CASE macro - is it better form to not quote
the keys?
Currently I have
       (case key-sym
         ('SHIFT_L         (setf *shift-down* val))
         ('SHIFT_R         (setf *shift-down* val))
         ...)
but I think you are saying that
       (case key-sym
         (SHIFT_L         (setf *shift-down* val))
         (SHIFT_R         (setf *shift-down* val))
         ...)
would work the same.  Which is better form?  I would have thought the
first example because everywhere in the code that I refer to SHIFT_L I
am using quote.  Does that mean that my case statement is not actually
working?  Or do ''a and 'a evaluate to the same symbol.

Cheers
Brad
From: Zach Beane
Subject: Re: Code critique request.
Date: 
Message-ID: <m3vez33wgj.fsf@unnamed.xach.com>
"bradb" <··············@gmail.com> writes:

> Thanks for taking the time to look over the code, the feedback is good.
> I have a question about the CASE macro - is it better form to not quote
> the keys?

CASE uses an unevaluated list designator in the key position. If the
keyform matches one of the keys in the key list, the corresponding
forms are evaluated. When you write this:

   (case foo
     ('bar ...)
     ...)

It's equivalent to:

   (case foo
     ((quote bar) ...)
     ...)

In that case, if FOO evaluates to the symbol QUOTE, the first form
will match, which is not usually what you want. Consider this:

   (defun foo (var) 
     (case var
       ('foo 1)
       ('bar 2)
       ('quote 3)))

   (list (foo 'foo) (foo 'bar) (foo 'quote)) => (1 2 1) ;; NOT (1 2 3)

See http://www.lispworks.com/documentation/HyperSpec/Body/m_case_.htm
for the full scoop.

> Which is better form?  I would have thought the first example
> because everywhere in the code that I refer to SHIFT_L I am using
> quote.

You need to use it in a way that is compatible with the evaluation (or
lack of evaluation) of the form you're using. CASE does not evaluate
its keys.

Zach
From: bradb
Subject: Re: Code critique request.
Date: 
Message-ID: <1131470796.046721.178600@g47g2000cwa.googlegroups.com>
Hmm, I don't think I quite get it yet.  In my code, I refer to the
symbol SHIFT_L, normally I need it refer to it using 'SHIFT_L, the
quote being required to prevent evaluation of SHIFT_L.  Therefore,
since CASE doesn't evaluate its keys, I should NOT quote them,
otherwise I am effectively comparing ''SHIFT_L to 'SHIFT_L.
So I think that the correct thing to do is not quote in the case.  But
how does your foo example matches any input that you give it?  Is it
not comparing (for the first call, evaluation done)
(case foo
 ('foo .....)

The hyperspec made my head hurt :)

Cheers
Brad
From: Thomas A. Russ
Subject: Re: Code critique request.
Date: 
Message-ID: <ymipspbq9v6.fsf@sevak.isi.edu>
"bradb" <··············@gmail.com> writes:

> 
> Hmm, I don't think I quite get it yet.  In my code, I refer to the
> symbol SHIFT_L, normally I need it refer to it using 'SHIFT_L, the
> quote being required to prevent evaluation of SHIFT_L.  Therefore,
> since CASE doesn't evaluate its keys, I should NOT quote them,
> otherwise I am effectively comparing ''SHIFT_L to 'SHIFT_L.

OK, one of the fundamental things one needs to learn when using Lisp is
the concept of argument evaluation.  That is because there are numerous
places in Lisp where arguments are NOT evaluated.  Macros normally do
not evaluate (all of) their arguments and some special forms don't
either.

You've already encoutered this, perhaps without realizing it, in the
definition forms, which are just fancy Lisp macros.  You write

  (defun foo (x y z) ...)

and not

  (defun 'foo '(x y z) ...)

Now this perhaps did not strike you as unusual, since most programming
languages have special constructs for defining things, which also do not
evaluate their arguments.  What is different about Lisp is the
consistency principle.  The definition forms are ordinary macros which
expand into code.  That is what makes it easy to extend the language
with your own definition forms to create domain-specific or
problem-specific languages that make solving large problems easier.

At any rate, the point is that you will need to think about what
arguments get evaluated and which ones don't.  That is where quoting
comes in.  Quoting is used to inhibit evaluation that you don't want,
particularly in the case of wanting to refer to a symbol as the symbol
object rather than as the name of some variable.

Compare:
  (let ((foo 42))
     (print 'foo)
     (print foo)
     ())

(I put the last NIL in because otherwise the example can be confusing,
since the final PRINT would both print the value of FOO and return it,
causing the read-eval-print loop to print it again.)

> So I think that the correct thing to do is not quote in the case.  But
> how does your foo example matches any input that you give it?  Is it
> not comparing (for the first call, evaluation done)
> (case foo
>  ('foo .....)

OK, you're missing part of Marco's example that is crucial to
understanding what is going on:

(let ((foo 'QUOTE))
   ;; Here we bind FOO to the symbol QUOTE.  Note that the symbol is a
   ;; different object from the special operator that it names.
   (case foo
     ('ANYTHING  (print "I'm chosen") foo)))

Evaluting this will print the message "I'm chosen" and then return the
value QUOTE.  The reason this happens is because the above form is
equivalent to

(let ((foo 'QUOTE))
   ;; Here we bind FOO to the symbol QUOTE.  Note that the symbol is a
   ;; different object from the special operator that it names.
   (case foo
     ((QUOTE ANYTHING)  (print "I'm chosen") foo)))

and that means that if the value of FOO is either the symbol QUOTE or
the symbol ANYTHING, the clause is executed.

This is because the syntax 'SYMBOL is just a reader macro shortcut for
writing the full form (QUOTE SYMBOL) instead.  This is done because
quoting symbols occurs frequently enough that a shorthand notation is
convenient.  The use of #'A-FUNCTION in place of (FUNCTION A-FUNCTION)
is a similar shortcut.


> 
> The hyperspec made my head hurt :)

Well, it's a standards document rather than a tutorial, so this isn't
too surprising.  Standards need to be written in a form of technical
"legalese" so that the meaning is precise.

There are several introductory texts for Lisp that guide you through the
process of learning some of the fundamental concepts that are keys to
understanding how to use the language.

-- 
Thomas A. Russ,  USC/Information Sciences Institute
From: bradb
Subject: Re: Code critique request.
Date: 
Message-ID: <1131475568.588399.135000@g47g2000cwa.googlegroups.com>
Thank you for taking the time to reply - your explaination is very
clear.

Thanks
Brad
From: Marco Antoniotti
Subject: Re: Code critique request.
Date: 
Message-ID: <K15cf.63$pa3.24289@typhoon.nyu.edu>
bradb wrote:
> Thanks for taking the time to look over the code, the feedback is good.
> I have a question about the CASE macro - is it better form to not quote
> the keys?
> Currently I have
>        (case key-sym
>          ('SHIFT_L         (setf *shift-down* val))
>          ('SHIFT_R         (setf *shift-down* val))
>          ...)
> but I think you are saying that
>        (case key-sym
>          (SHIFT_L         (setf *shift-down* val))
>          (SHIFT_R         (setf *shift-down* val))
>          ...)
> would work the same.  Which is better form?  I would have thought the
> first example because everywhere in the code that I refer to SHIFT_L I
> am using quote.  Does that mean that my case statement is not actually
> working?  Or do ''a and 'a evaluate to the same symbol.

If you use

	(case key-sym
	   ('shift_l ....))

then you may as well use

	(case key-sym
	   ((quote shift_l) ...))

which is what the reader actually expands into.  Now, the above works 
because CASE and friends accept a list of constants as tags in order to 
handle unions of cases.  Think

	switch (foo)
	{
	   case 1:
	   case 2:
	   case 3:
	      printf("%d\n", foo + 42);
	      break;
	   case 42:
	      printf("forty-two\n");
	      break
	}

Just to try you can run the following code

	(let ((foo 'quote))
	  (case foo
	     ('forty-two 42)))

Check also the CLHS for CASE and friends.

Cheers
--
Marco
From: bradb
Subject: Re: Code critique request.
Date: 
Message-ID: <1131471019.338395.286380@z14g2000cwz.googlegroups.com>
Ah - enlightenment!!  Thanks Marco.  I think that I am going to need to
learn to expand macros & think about their expanded consequences.
In this case, the issues are
1) 'foo expands to (quote foo)
2) CASE accepts lists of keys to match without evaluation
3) (quote foo) is a vaild key list & is wrong

Thanks!!
Brad
From: Marco Antoniotti
Subject: Re: Code critique request.
Date: 
Message-ID: <dS5cf.65$pa3.24274@typhoon.nyu.edu>
De nada compadre! :)

Cheers
--
Marco




bradb wrote:
> Ah - enlightenment!!  Thanks Marco.  I think that I am going to need to
> learn to expand macros & think about their expanded consequences.
> In this case, the issues are
> 1) 'foo expands to (quote foo)
> 2) CASE accepts lists of keys to match without evaluation
> 3) (quote foo) is a vaild key list & is wrong
> 
> Thanks!!
> Brad
>