most of the CLHS functions?
'almost pure' functional programming?
It seems to me you could develop sophisticated software using just a
couple of these techniques without even scratching the surface of the
Write software. Use it. Fix it. Enhance it. Do it better next the
next time than you did the last time.
What more could you want?
But to answer your question, I'd say knowing Lisp starts when you stop
writing C with it.
Sure, you could develop sophisticated software without using ANY of
these techniques, but why would you want to? All of these techniques
are there to help you write more reliable and maintainable software
faster. Therefore, the more such techniques you know, the better.
"really knowing Lisp" is not a binary thing. Very few people
master all of what CL can offer, and most of us discover new features
on a regular basis.
I am not sure about the motivation for your question. Would you like
for someone to tell you that you can safely ignore some features and
still "really know Lisp"? In my opinion, that is not a good idea.
You might want to take a look at a little essay that I wrote a few
years ago about the psychology of learning:
I also tell my students they can be productive writing
software in languages they don't completely understand.
This is easier in some languages than in others, of
course. C++ is notably difficult for beginners because
so much goes on behind the scenes, and because so much
of that implicit stuff is semantically important.
Yes you can , many people were doing that or still doing this
in less capable languages (looking from Lisp perspective that is every
other language) . But if a tool could help you why not using it ?
I personally hate OO but when it's the right way i use it.
You can develop sophisticated software using just assembler. That's not
the point. A programming language is a human-computer interface. It's
important to be able to express a solution to a problem in terms that
2nd European Lisp and Scheme Workshop
July 26 - Glasgow, Scotland - co-located with ECOOP 2005
Robert Strandh wrote:
> I am not sure about the motivation for your question. Would you like
> for someone to tell you that you can safely ignore some features and
> still "really know Lisp"? In my opinion, that is not a good idea.
My motivation is as I learn the language, I will have ways to measure
my progress and plan my next goal to achieve. Since the language and
the techniques that come with it can be so different from those I know
now, I think this would be helpful.
Greg Menke wrote:
> But to answer your question, I'd say knowing Lisp starts when you stop
> writing C with it.
That's one of the explanations I'm having trouble with. Does this mean
writing more functionally, and using the more complex data structures
(hashes, lists, CLOS) and tools (mapping, sequence manipulation)?
I don't think you can really learn Lisp (or much of anything) in such a
piecemeal fashion. You need to learn just enough about the different
parts of Lisp to know how to use them, and then practice, practice,
practice, practice, practice.
Learn enough about macros to feel that you could write many of the
macros that are already in COMMON-LISP. In particular, DOLIST, DO*, DO,
and LET* are good practice macros.
Learn enough about closures to understand how FLET, LABELS, and LAMBDA
Learn enough about CLOS to understand how you write OO software in it,
and how generic functions are more powerful than virtual functions.
Then start writing actual software, with an eye toward removing code
duplication. This is the most important step. Lisp's features allow
you to abstract away patterns in simple, direct, yet fast ways that no
other language has.
I'd say writing in Lisp is more associated with using macros and
closures to simplify code. Since C doesn't offer much for those two
cases, its code has a tendency towards highly specified rigidity.
Maybe to put it another way, once your Lisp experience starts making
your C/C++ code better, then you're learning Lisp.
The first signs of starting to grok Lisp might be you suddenly discover
that you're being overly obsessed with types. For instance you can just
let numbers be numbers until you care about presenting them.
> Learn enough about macros to feel that you could write many of the
> macros that are already in COMMON-LISP. In particular, DOLIST, DO*,
> DO, and LET* are good practice macros.
Just in case anyone takes this up: LET*, I believe, cannot be
implemented as a macro absent semantic knowledge of declarations at
macroexpansion time. LET* without declarations is a good practice
I'm still a lisp newbie, working on exercises from ACL and PCL, making
an effort to absorb knowledge and ideas from c.l.l.
At work, I'm implementing a SOAP gateway to an Oracle database in java.
It involves an annoying amount of cut-and-paste code, to set up and
close database connections, and to catch the exact same error
conditions for every single table lookup. At one point, I got annoyed,
and thought to myself, "With a 'with-database-connection' macro, this
class would be a tenth of the size."
...and then I realized that maybe I'm starting to grok some of this
lisp stuff after all.
Thats a good beginning.
> ...and then I realized that maybe I'm starting to grok some of this
> lisp stuff after all.
As far as that goes, set up a Postgres server someplace, load your data
into it- skip SOAP & Java and manipulate the tables & queries directly
from Lisp- theres a Postgres interface on cliki last time I checked
which seemed like it would do the trick. Or maybe check out the ODBC
interfaces available in the commerical Lisps.
You'll probably notice a deafening lack of the loud sucking noise that I
imagine you've been getting used to.
``What defines *really* knowing Lisp, in your opinion?''
Waking up with a hangover and finding parenthesis mixed in with your
I had weird parenthesis dreams when I was first seriously learning
Lisp, but they went away when I came to better know Lisp.
How about this? Use SYMBOL-MACROLET to rename the variables to gensyms:
(defmacro let*- (bindings &body body)
(let ((gensyms (mapcar (lambda (binding)
(gensym (symbol-name (first binding))))
(let*-helper bindings gensyms 0 body)))
(defun let*-helper (bindings gensyms n body)
(if (= n (length bindings))
`(let ,(map 'list (lambda (binding gensym)
(list (first binding) gensym))
(let ((value (second (nth n bindings)))
(gensym (nth n gensyms))
(prev-vars (mapcar #'first (subseq bindings 0 n)))
(prev-gensyms (subseq gensyms 0 n)))
`(let ((,gensym (symbol-macrolet ,(map 'list
(lambda (var gensym)
(list var gensym))
,(let*-helper bindings gensyms (1+ n) body)))))
CL-USER> (macroexpand-1 '(let*- ((a 1)
(b (1+ a)))
(values a b)))
(SYMBOL-MACROLET ((A #:A4714))
(LET ((A #:A4714) (B #:B4715))
(VALUES A B))))
CL-USER> (let*- ((a 1)
(b (1+ a)))
(values a b))
> Christophe Rhodes wrote:
>> M Jared Finder <ja...@hpalace.com> writes:
>>>Learn enough about macros to feel that you could write many of the
>>>macros that are already in COMMON-LISP. In particular, DOLIST, DO*,
>>>DO, and LET* are good practice macros.
>> Just in case anyone takes this up: LET*, I believe, cannot be
>> implemented as a macro absent semantic knowledge of declarations at
>> macroexpansion time. LET* without declarations is a good practice
> How about this? Use SYMBOL-MACROLET to rename the variables to gensyms:
What problem is that meant to solve? Now I'm confused :-)
For the declaration-free let*, why doesn't
(defmacro let% (bindings &body body)
(if (null bindings)
`(let (,(car bindings))
(let% (,@(cdr bindings)) ,@body))))
(The difficult bit with declarations is to get something like
(flet ((foo () (declare (special x)) (1+ x)))
(let* ((x 1)
(declare (special x))))
>>> Just in case anyone takes this up: LET*, I believe, cannot be
>>> implemented as a macro absent semantic knowledge of declarations
>>> at macroexpansion time. LET* without declarations is a good
>>> practice macro.
> (The difficult bit with declarations is to get something like
> (flet ((foo () (declare (special x)) (1+ x)))
> (let* ((x 1)
> (y (foo)))
> (declare (special x))))
So maybe this is what you mean by "semantic knowledge of declarations"
or maybe I'm still missing something but this handles your test case
(defmacro let% ((&rest bindings) &body body)
(if (null bindings)
(destructuring-bind ((variable init-form) &rest remaining-bindings)
(multiple-value-bind (declarations body)
(multiple-value-bind (applicable-declarations not-applicable-declarations)
(applicable-declarations variable declarations)
`(let ((,variable ,init-form))
,@(append not-applicable-declarations body))))))))
(defun normalize-bindings (bindings)
(mapcar #'(lambda (b) (if (consp b) b (list b nil))) bindings))
(defun parse-declarations (body)
(let ((real-body (member 'declare body :key #'car :test-not #'eql)))
(let ((declarations (ldiff body real-body)))
(values (expand-declarations declarations) real-body))))
(defun expand-declarations (declarations)
(loop for decl in declarations nconcing (expand-1-declaration decl)))
(defun expand-1-declaration (declaration)
(if (eql (caadr declaration) 'type)
(loop with type = (cadadr declaration)
for var in (cddadr declaration) collect `(declaration (,type ,var)))
(loop with d = (caadr declaration)
for var in (cdadr declaration) collect `(declare (,d ,var)))))
(defun applicable-declarations (variable declarations)
(loop for d in declarations
when (eql (cadadr d) variable) collect d into applicable
else collect d into not-applicable
finally (return (values applicable not-applicable))))
I was trying to support declarations by delaying the binding of the
variables until the last block. Doesn't that solve the problem with
let* and declarations?