This is a read-only archive of lispforum.com. The forum was locked to new users and posts and is preserved here as static HTML from a database snapshot taken on 2019-09-07.

Experience of Learning Lisp

53 posts · 24158 views

I am in the process of learning Lisp, and am interested in the experience other people had while learning it.

What drew me to Lisp - as a programmer of 15 years (10 professionally), I always like to learn new things and expand my horizons. Sadly, as a LotusScript (if you're not familiar with it, think Visual Basic of 1995, and if you're not familiar with that, just think crap) programmer of 10 years, Javascript, Java, and C# have been the extent of the new things that I've had at least a little experience with to expand my horizons. I've recently come across Paul Graham's writings and others, and the idea that learning Lisp would change my thinking (it already has) and the idea of code as data strongly appealed to me.

I'm currently still in the very early stages (Ch 13 of Practical Common Lisp) of learning, but have a lot of thoughts going through my head.

How long did it take you to get started? I've been going at a fairly slow pace (only up to Ch 13 after 2-3 weeks), but Lisp is still almost completely foreign to me. It's like looking at Perl. Although I have limited experience using languages, I read a lot of code in various languages, and it's generally pretty easy to get an idea of what's going on. Not so with Lisp. So many functions have cryptic names that are on par with assembly for understandability. And how many functions there are! I'm getting the impression that Lisp offers the functionality of Java plus a good chunk of Java's libraries all within the language spec. So... that's great, but a troublesome downside for a newcomer is the sheer quantity of functions, many with cryptic names with no organization - everything's just there, like Java keywords. I appreciate the logical hierarchy and organization imposed on methods in an OO design. (Before I get too far, I am aware that Lisp supports objects, but what I'm addressing here is the large quantity of functions in the language spec that would be much more comprehensible and easier to learn if they had some sort of organization.)

I feel like my first act after learning enough Lisp to get started would be to rewrite Lisp. (And WOW is it pleasing that you can actually do that in Lisp!) Create macros to give functions names that are meaningful to English speakers. Learning Lisp is like learning a whole new foreign language - knowing English is not sufficient to deduce the purpose and behavior of many Lisp functions simply from their name. And organization - if a group of functions only operates on lists, then perhaps name them list-add(), list-remove()? For the similar functions that operate on trees, or plists, or whatever, give them similarly clear names.

While I'm at it, I might hide some of the inconsistencies. It seems like Lisp's destructive functions are wantonly destructive! Why no be productively destructive? I was completely confused when I discovered that you still had to use the return value of destructive functions:
(setf *list-3* (delete 4 *list-3*))
If delete is destructive, why?! must I use setf I railed. It was a good bit more reading before I realized that destructive functions destroy with no (apparent) rhyme or reason, and the destroyed parameter is different from the value returned. I would be inclined to replace all these functions with functions that modify the parameter to produce the desired result, rather than requiring the caller to use the function's return value for the desired results and trashing the parameter!

As early as I am in my process of learning Lisp, I am very excited by it, and dumbfounded that we ended up with popular languages we've suffered with for so many decades, while Lisp sat there quietly in the shadows. It didn't take too much experience programming before I was frustrated by the fact that I couldn't tell my code to modify code or write code! I mean seriously, why won't they let me do that?! When reflection finally became popular, that was at least some small relief, but the full power of Lisp's macros is sorely needed!

All these thoughts lead me to wonder... has someone tried to "clean up" Lisp? Is there some reason it's a bad idea? It seems cruel to me that the amazing power of Lisp is hidden away in a cryptic language riddled with idiosyncrasies and inconsistencies. Surely someone else has had a go at improving the situation...? It seems like with some macros and code organization, Lisp could become far more accessible to the large numbers of C/C++/C#, Java, and even Basic programmers out there.

Given the current business environment I suspect I'll be fortunate if I'm even able to use the JVM-friendly Clojure, with Common Lisp being a distant pipe-dream (unless I get a new job, which Lisp just might encourage me to attempt). How similar is Clojure to Common Lisp? Have they attempted to smooth out some of the rough edges?

Re: Experience of Learning Lisp

mrove wrote:I am in the process of learning Lisp, and am interested in the experience other people had while learning it.
Congrats! You'll thank yourself later. :D
How long did it take you to get started? I've been going at a fairly slow pace (only up to Ch 13 after 2-3 weeks), but Lisp is still almost completely foreign to me. It's like looking at Perl. Although I have limited experience using languages, I read a lot of code in various languages, and it's generally pretty easy to get an idea of what's going on. Not so with Lisp.
You can learn basic Lisp syntax fairly quickly, but the advanced features will definitely take you a while. IMO, PCL is a good book, but I actually like starting with Graham's ANSI Common Lisp first, followed by PCL.
So many functions have cryptic names that are on par with assembly for understandability. And how many functions there are! I'm getting the impression that Lisp offers the functionality of Java plus a good chunk of Java's libraries all within the language spec. So... that's great, but a troublesome downside for a newcomer is the sheer quantity of functions, many with cryptic names with no organization - everything's just there, like Java keywords. I appreciate the logical hierarchy and organization imposed on methods in an OO design. (Before I get too far, I am aware that Lisp supports objects, but what I'm addressing here is the large quantity of functions in the language spec that would be much more comprehensible and easier to learn if they had some sort of organization.)
The good news is that the actual core of Lisp is relatively small by the standards of almost any other language. Most of the CL spec is really library functions. The downside is that because Lisp is old, many of the names of library functions are archaic and crufty. More than once, I have found myself reimplementing something already in the standard library simply because I didn't realize it was there because it lived under a name I wasn't expecting. I don't know a way to prevent that other than working with the language for a while.
I feel like my first act after learning enough Lisp to get started would be to rewrite Lisp. (And WOW is it pleasing that you can actually do that in Lisp!) Create macros to give functions names that are meaningful to English speakers. Learning Lisp is like learning a whole new foreign language - knowing English is not sufficient to deduce the purpose and behavior of many Lisp functions simply from their name. And organization - if a group of functions only operates on lists, then perhaps name them list-add(), list-remove()? For the similar functions that operate on trees, or plists, or whatever, give them similarly clear names.
You can do this, but I'd warn you away from using it as a long-term crutch. As I have suggested before, when you learn French or Japanese, it's better to try to speak them with a native accent, not your foreign accent. It might seem more rational to you to do something one way or another, but the native speakers will be very annoyed at you. As a short-term crutch while you are learning, go ahead. As you said, Lisp makes it possible, so have fun. But realize that you'll be trading off some long-term items.
While I'm at it, I might hide some of the inconsistencies. It seems like Lisp's destructive functions are wantonly destructive! Why no be productively destructive? I was completely confused when I discovered that you still had to use the return value of destructive functions:
(setf *list-3* (delete 4 *list-3*))
If delete is destructive, why?! must I use setf I railed. It was a good bit more reading before I realized that destructive functions destroy with no (apparent) rhyme or reason, and the destroyed parameter is different from the value returned. I would be inclined to replace all these functions with functions that modify the parameter to produce the desired result, rather than requiring the caller to use the function's return value for the desired results and trashing the parameter!
I originally thought the same thing. The nice thing is that you'll find that Lisp is actually more consistent this way: you should use SETF with all those functions, whether they are destructive or not. This also makes it easier sometimes to go in and optimize code, moving from a more functional style, without destructive functions, to using the destructive versions. You simply have to change a function name, after checking that using a destructive function is actually safe, and you're golden. That said, I agree that this confused me several times at first. Graham's ANSI Common Lisp actually does a good job with describing this.
As early as I am in my process of learning Lisp, I am very excited by it, and dumbfounded that we ended up with popular languages we've suffered with for so many decades, while Lisp sat there quietly in the shadows. It didn't take too much experience programming before I was frustrated by the fact that I couldn't tell my code to modify code or write code! I mean seriously, why won't they let me do that?! When reflection finally became popular, that was at least some small relief, but the full power of Lisp's macros is sorely needed!
Yup. Your eyes will be progressively opened as you delve in deeper. I think every Lisper reaches this point where the lightbulb goes on and you say, "Wow, I can't believe I missed this language all this time. Where has it been hiding?" :D
All these thoughts lead me to wonder... has someone tried to "clean up" Lisp? Is there some reason it's a bad idea? It seems cruel to me that the amazing power of Lisp is hidden away in a cryptic language riddled with idiosyncrasies and inconsistencies. Surely someone else has had a go at improving the situation...? It seems like with some macros and code organization, Lisp could become far more accessible to the large numbers of C/C++/C#, Java, and even Basic programmers out there.
Well, yes, sort of. Scheme is more clean than Common Lisp. Clojure is a modern Lisp.

That said, Common Lisp is sort of like English: it has a long history and as a result, lot of inconsistencies and exceptions to the rule, but everybody knows it and in practice it's very powerful. A young Finnish kid on a train outside Helsinki once told me, "English is the easiest language in the world to learn to speak poorly. It's the most difficult language in the world to learn to speak well." The same might be said of Common Lisp, I suppose. While people have talked about cleaning up Common Lisp for a while, and there have been several serious attempts at it (Google for ISLISP, for instance), in practice the challenge would be similar to trying to "clean up" English. You're better of making a clean break and starting fresh, as did Scheme and Clojure, using the general ideas that go along with Lisp and reimplementing the standard library to suit.
Given the current business environment I suspect I'll be fortunate if I'm even able to use the JVM-friendly Clojure, with Common Lisp being a distant pipe-dream (unless I get a new job, which Lisp just might encourage me to attempt). How similar is Clojure to Common Lisp? Have they attempted to smooth out some of the rough edges?
From what I can tell (not having done a lot in Clojure), it smooths out a lot of rough edges, but introduces some idiosyncrasies of its own. That said, there is a version of CL that runs on the JVM (Google for "Armed Bear Common Lisp," ABCL).
Cheers, Dave
Slowly but surely the world is finding Lisp. http://www.findinglisp.com/blog/

Re: Experience of Learning Lisp

mrove wrote: All these thoughts lead me to wonder... has someone tried to "clean up" Lisp?
Yes. Every single newbie, before they began to see a significant part of the whole thing.
"Just throw more hardware at it" is the root of all evil.
Svante

Re: Experience of Learning Lisp

If you do go on a cleanup expedition, here are a few suggestions.
- Keep the parens.
- Start by splitting things into smaller packages. e.g. ansi.alist, ansi.plist, ansi.cons, ...
- Keep names like CAR and CDR, but rename things like MAPCAN.
- Use symbol macros in these subpackages to redirect back to the symbols in CL; this way you don't have to worry about recreating argument lists, avoid any inefficiencies, and don't lose docstrings.
- Pick a reader (such as on http://www.informatimago.com/develop/lisp/), and make it part of your standard. Document places to make it more extensible.

I first saw the word "peloria" when Kenny Tilton used it to describe many attempts at reworking CL by regularizing argument lists.

Re: Experience of Learning Lisp

mrove wrote:While I'm at it, I might hide some of the inconsistencies. It seems like Lisp's destructive functions are wantonly destructive! Why no be productively destructive? I was completely confused when I discovered that you still had to use the return value of destructive functions:
(setf *list-3* (delete 4 *list-3*))
If delete is destructive, why?! must I use setf I railed.
Because in lisp there is no "pass variable by reference" to a function. If you need to modify the value of some variable, you need either use setf or a macro. This is a characteristic of functional style, and, probably, in a few months, you will think, like me, that passing variables "by reference" is strongly associated with headaches ;)

Note that, in order to create a function like delete that changes the second variable (so you can avoid using setf), the function would need to modify the car and the cdr of *list-3*. And, if the result was nil, that wouldn't work as well.

But, if you think harder, you will see that, if you have macros, you don't need to complicate things. Just use them:
(defmacro deletef (list item)
  `(setf ,list (delete ,item ,list)))
I swapped the order of the arguments because it is more or less of a convention that modifying arguments be the first argument of you macro (the terminating 'f' is another convention for modifying macros like this one).

Some utilities libraries (like alexandria) provide some macros that does what you want. It also swaps the order of the arguments, like I did:
cl-user> (defvar *list* (list 1 2 3 4 5))
*list*
cl-user> (deletef *list* 1)
(2 3 4 5)
cl-user> *list*
(2 3 4 5)
Hum... By the way, talking about Alexandria:

"As a project Alexandria's goal is to reduce duplication of effort and improve portability of Common Lisp code according to its own idiosyncratic and rather conservative aesthetic."

Maybe Alexandria is more or less what you are looking for?

There is also CDR which tries to establish new standards, but nothing was achieved by CDR up to now (that I know of).
mrove wrote:All these thoughts lead me to wonder... has someone tried to "clean up" Lisp? Is there some reason it's a bad idea? It seems cruel to me that the amazing power of Lisp is hidden away in a cryptic language riddled with idiosyncrasies and inconsistencies. Surely someone else has had a go at improving the situation...? It seems like with some macros and code organization, Lisp could become far more accessible to the large numbers of C/C++/C#, Java, and even Basic programmers out there.
Well, many people try to "clean up" Lisp in several ways. Most of them are just newbies that know very little and end up doing a terrible, useless job. I believe that it isn't Lisp what needs to be cleaned up, but people need to get used to it. Just give yourself time to try and you will see Lisp as a very natural language, and it doesn't have as much riddles, idiosyncrasies and inconsistencies as you think. It surely is not perfect, but it is far away from a bad language. IMO, the only really bad thing about Common Lisp is the lack of modern standardization (to standardize things like threads, sockets, gray-streams and other stuff).

Re: Experience of Learning Lisp

Most of your questions have already been answered well, so I'm just throwing in some bits:
mrove wrote:I'm currently still in the very early stages (Ch 13 of Practical Common Lisp) of learning, but have a lot of thoughts going through my head.
Use Lisp to give your thoughts form. Write software in Lisp. This will greatly accelerate your learning.
How long did it take you to get started? I've been going at a fairly slow pace (only up to Ch 13 after 2-3 weeks), but Lisp is still almost completely foreign to me.
After almost two years I've grown familiar with Lisp.
It's like looking at Perl.
Only with Lisp there are great patterns behind it, and at some point you will discover that the core principles of Lisp are surprisingly simple and uniform compared to other languages.

but what I'm addressing here is the large quantity of functions in the language spec that would be much more comprehensible and easier to learn if they had some sort of organization.)
But they are organized. Browsing the HyperSpec you'll find everything neatly organized into categories.
It seems like with some macros and code organization, Lisp could become far more accessible to the large numbers of C/C++/C#, Java, and even Basic programmers out there.
Lisp requires a different mindset, but there are projects like NewLISP (targeting PHP programmers) and Clojure (targeting Java programmers).
Given the current business environment I suspect I'll be fortunate if I'm even able to use the JVM-friendly Clojure, with Common Lisp being a distant pipe-dream (unless I get a new job, which Lisp just might encourage me to attempt).
Web applications can be written in any Lisp you like.

Leslie

Re: Experience of Learning Lisp

mrove wrote:I appreciate the logical hierarchy and organization imposed on methods in an OO design. (Before I get too far, I am aware that Lisp supports objects, but what I'm addressing here is the large quantity of functions in the language spec that would be much more comprehensible and easier to learn if they had some sort of organization.)
On the plus side, it means that they're just there — you don't have to obtain or know about some particular sort of object to use them. And they're fairly well organized in the Hyperspec. Just check the relevant “dictionary” section. For example, maybe you look in the Strings Dictionary for string-related functions.
mrove wrote:I feel like my first act after learning enough Lisp to get started would be to rewrite Lisp. (And WOW is it pleasing that you can actually do that in Lisp!) Create macros to give functions names that are meaningful to English speakers.
...and lose the ability to pass them as functions. If it's a function to begin with, turning it into a macro is just going to make things awkward.
mrove wrote:Learning Lisp is like learning a whole new foreign language - knowing English is not sufficient to deduce the purpose and behavior of many Lisp functions simply from their name. And organization - if a group of functions only operates on lists, then perhaps name them list-add(), list-remove()? For the similar functions that operate on trees, or plists, or whatever, give them similarly clear names.
And then your clean, readable code turns into list- list- list- list-? Seems like a net lose.
mrove wrote:While I'm at it, I might hide some of the inconsistencies. It seems like Lisp's destructive functions are wantonly destructive! Why no be productively destructive? I was completely confused when I discovered that you still had to use the return value of destructive functions:
(setf *list-3* (delete 4 *list-3*))
If delete is destructive, why?! must I use setf I railed. It was a good bit more reading before I realized that destructive functions destroy with no (apparent) rhyme or reason, and the destroyed parameter is different from the value returned. I would be inclined to replace all these functions with functions that modify the parameter to produce the desired result, rather than requiring the caller to use the function's return value for the desired results and trashing the parameter!
Since they are functions they're not able to modify the parameter even if they want to. You'd have to use macros for that, but then you wouldn't be able to pass them as parameters to higher-order functions. And most of the time the input to these functions isn't stored in a variable anyway. So what are we going to do, check every call to see if the argument is a variable, or make it incompatible with the most common use case?

The destructive functions are efficiency hacks. They don't exist for side effects; they are allowed to have side effects in the name of efficiency. So why do extra work to set the original variable to the return value when the programmer probably just wants the return value anyway? Because in Lisp you're probably either nesting function calls, establishing a new binding with “let”, or using some looping construct, right? How often are you putting your values into variables then modifying them there?
mrove wrote:All these thoughts lead me to wonder... has someone tried to "clean up" Lisp? Is there some reason it's a bad idea? It seems cruel to me that the amazing power of Lisp is hidden away in a cryptic language riddled with idiosyncrasies and inconsistencies.
“A cryptic language riddled with idiosyncrasies and inconsistencies”? You make it sound like C++ or something. :P No doubt Lisp has its idiosyncrasies, but it's far from cryptic and inconsistent overall. Every language has its quirks, and I don't think Common Lisp has more than its share.

There are lots of things about Common Lisp that could stand to be cleaned up (such as combining semi-redundant functions, making the built-in functions generic, and reducing the complexity of filenames since file systems seem to have settled down), but updating the standard is expensive and there are still plenty of people with PTSD from creating the Common Lisp standard in the first place. Common Lisp the standard isn't changing any time soon, and I think most long-time Lispers feel that the difficulty of making up and implementing a new Lisp that functions as well as Common Lisp is much greater than the benefit derived from doing so. Of course, some people might say that Clojure or Scheme is just that Lisp, but personally I think CL's stability, maturity, and the synergy between its features are big assets that I want in the Lisp I'm using.

Re: Experience of Learning Lisp

How long did it take you to get started?
Complex question, given that I was learning a big gestalt of stuff at that point. (I found computing beyond boring, so I looked around for its possiblities. Why can't programming be as entertaining as any other computer game?) Also, I then considered improved skill with emacs and linux to be a prerequisite for seriously using Common Lisp.

I've been going at a fairly slow pace (only up to Ch 13 after 2-3 weeks), but Lisp is still almost completely foreign to me.
A chapter a day or so isn't slow. ;) Also, ideally you'd have some sort of mentoring.

It's like looking at Perl. Although I have limited experience using languages, I read a lot of code in various languages, and it's generally pretty easy to get an idea of what's going on.
Readability's an interesting topic. One fortunate thing is that you can ask the Lisp environment questions about what you read, using (say) the REPL. This resource is not captured in most discussions of readability. With computers and programming languages, the notion of readability is expanded to include being able to ask certain questions (and expect a response), as well as change the context which things execute under.

There are parts of Lisp which are unnecessarily difficult to read. For instance, forms like 'cond' are garnished with too many parentheses. Evolutions of Lisp like Clojure attempt to reduce this problem.

Also that there's a different reading style, since people using Lisp often just pass results from one operator to another, rather than accumulate stuff in intermediate variables all the time. So instead of vertical towers of:

thing = this
thing = that

you see strings of:

<- <- <- <-

Though there are exceptions; my uses of CL's 'loop' look more like the former.

I'm curious how it'd be if Lisps used postfix. (Forth doesn't look so bad.)

And how many functions there are! I'm getting the impression that Lisp offers the functionality of Java plus a good chunk of Java's libraries all within the language spec. So... that's great, but a troublesome downside for a newcomer is the sheer quantity of functions, many with cryptic names with no organization - everything's just there, like Java keywords. I appreciate the logical hierarchy and organization imposed on methods in an OO design. (Before I get too far, I am aware that Lisp supports objects, but what I'm addressing here is the large quantity of functions in the language spec that would be much more comprehensible and easier to learn if they had some sort of organization.)
Yes, dumping everything in the same namespace is a bug. I remember hearing it was considered a known flaw during Common Lisp's standardization, but they didn't have the resources then to do anything about it. By that time, the participants were taking an economic battering.

That said, the back of Graham's _ANSI Common Lisp_ contains a very good reference for me, solving that problem to a great extent. I haven't really read the other parts of the book, but that quickref put everything in perspective. Maybe there's similar cheatsheets to download out there.

It reminds me of Patrick Chan's Java series, which I had similar views on.

It's true that many names are baroque; it's a bit like an old city where you can see artifacts from different generations and movements. Parts of which are definitely rough; others more well-developed.

If delete is destructive, why?! must I use setf I railed. It was a good bit more reading before I realized that destructive functions destroy with no (apparent) rhyme or reason, and the destroyed parameter is different from the value returned. I would be inclined to replace all these functions with functions that modify the parameter to produce the desired result, rather than requiring the caller to use the function's return value for the desired results and trashing the parameter!
Yeah, as I understand, the spec's semantics were loose here in order to give implementors leeway to make optimizations for their users. I just use the non-destructive versions; and consider the destructive versions as possible resources in weird situations. (Which I haven't come across.)

As early as I am in my process of learning Lisp, I am very excited by it, and dumbfounded that we ended up with popular languages we've suffered with for so many decades, while Lisp sat there quietly in the shadows. It didn't take too much experience programming before I was frustrated by the fact that I couldn't tell my code to modify code or write code! I mean seriously, why won't they let me do that?!
Yeah, it's still a bit of a bizarre feeling. We use these tools to automate. Why not try to complete the circle and let the tools have some ability to be used on themselves?

Given the current business environment I suspect I'll be fortunate if I'm even able to use the JVM-friendly Clojure, with Common Lisp being a distant pipe-dream (unless I get a new job, which Lisp just might encourage me to attempt). How similar is Clojure to Common Lisp? Have they attempted to smooth out some of the rough edges?
Oh, definitely. Clojure has evolved CL in a number of daring ways. And they've put an effort into cleanup. If you're ever jaded by CL, you might want to try Clojure. (And given that I've until recently used CL nearly every single day for.. longer than is healthy.. being jaded is an issue. I mean, part of programming is dead if you somehow can't improve on general-purpose languages.)

(I don't think I'm too biased in favor of cleanliness; Scheme is cleaner than CL, but I'd probably rather program in Python than any Scheme implementation I've tried.)

Dunno which Lisp you'll find more fun to use. And you can have a couple in your toolbox.

Re: Experience of Learning Lisp

mrove wrote:While I'm at it, I might hide some of the inconsistencies.
Like what? The only thing that bugs me is (file-position s n) instead of (setf (file-position s) n)

Re: Experience of Learning Lisp

Thanks a lot for all of your replies. I certainly realize that going into something new one's perspectives lack experience, so I'm grateful to get insights from people who have not only had the same (or similar) thoughts I am having, but have the benefit of experience and hindsight to balance things.

Re: Experience of Learning Lisp

Like what? The only thing that bugs me is (file-position s n) instead of (setf (file-position s) n)
Take a look at:
(cons    collection item)
(gethash item       collection)
(elt     collection item)
(nth     item       collection)
(aref    collection items)
(assoc   items      collection)
(getf    collection item)
I'm reminded of Dave Moon's comment (he worked on Lisp Machines): "By the way, Java takes a “the right thing” approach and Lisp takes a relatively “worse is better” approach. So what do you think of that rpg?"

And there was this evil little joke stuck in 'loop':
(loop for k being the hash-keys of h using (hash-value v)
      ...)
It's maybe funny the first 100 times you type it out. (Not common enough to dedicate an emacs hotkey, not rare enough to be ignored... Maybe I should've written loop-mode.) One nice thing about Clojure is that lists aren't privileged so much compared to other common general-purpose containers like maps, vectors and sets. Whereas using hashtables in CL is much less convenient.

Re: Experience of Learning Lisp

mrove wrote:Thanks a lot for all of your replies. I certainly realize that going into something new one's perspectives lack experience, so I'm grateful to get insights from people who have not only had the same (or similar) thoughts I am having, but have the benefit of experience and hindsight to balance things.
Do make frequent use of this forum to ask specific questions about Common Lisp code or design. This way you'll not only profit from thinking about problems yourself but also from the experience of others.

Re: Experience of Learning Lisp

(cons    collection item)
It's the other way round (because you cons an ITEM onto a COLLECTION).
(gethash item       collection)
(elt     collection item)
(nth     item       collection)
(aref    collection items)
(assoc   items      collection)
(getf    collection item)
Some of those are easily justifiable (e.g. GETF gets a place, GETHASH gets a value associcated with a hash) while others are not. It's not as incoherent as you made it to look here. It's also more simple in CL to look it up quickly, a simple (describe 'foo) in the REPL will set you straight.

Also note that NTH, ELT and AREF use indices while the others take different clues.
"By the way, Java takes a “the right thing” approach
...and is inflexible as hell (objects everywhere, interfaces instead of MI, ...).

Common Lisp gets things done, and gets them done effectively and in a way you can rely on. That's the point of it.

Re: Experience of Learning Lisp

It's the other way round (because you cons an ITEM onto a COLLECTION).
A wonderful example where CL's inconsistency can trip a user up, causing a typo on the most basic operator of the bunch. ;) The art of the meta-example...
Some of those are easily justifiable (e.g. GETF gets a place, GETHASH gets a value associcated with a hash) while others are not. It's not as incoherent as you made it to look here.
I suspect that the fact that some have easy justifications -- while others don't -- is precisely why it's 'inconsistent.' That's why we speak of inconsistency rather than (say) randomness or consistency.

(But it's possible my list is glib or obscuring some axis of consistency, dunno. I haven't used CL much this year.)
Common Lisp gets things done, and gets them done effectively and in a way you can rely on. That's the point of it.
I pity the poor person who argues with that statement on a primarily Common Lisp forum.

I wrote a page listing "Lisp gotchas", but unfortunately something bad happened with the ALU wiki and I can't find it... (Was it programmed in Common Lisp? If so, that may have been less maintainable than going with an inexpensive service coded in a more mainstream language. For example, this Lisp forum was implemented in PHP.) I find that I've forgotten many of CL's inconsistencies, since I stopped using it this year.

This is not to say that CL is any less inconsistent than others; after all, it's one of my two favorite languages, and the inconsistencies are relatively insignificant though noticeable. (The other language is a descendant of CL, and I have no judgement on which is 'better' in some absolute sense.) The focus of this conversation is that you indeed have more power to fix inconsistencies.

Re: Experience of Learning Lisp

[duplicate post removed]

Last edited by tayssir on , edited 1 time in total.

Re: Experience of Learning Lisp

tayssir wrote:
It's the other way round (because you cons an ITEM onto a COLLECTION).
A wonderful example where CL's inconsistency can trip a user up, causing a typo on the most basic operator of the bunch. ;) The art of the meta-example...
I think that this just shows that you have not understood CONS. Note that a list is just a shorthand for a bunch of cons cells connected in a defined way. (cons collection item) is not necessarily a mistake---unless you expected the collection to be a proper list before and after.
"Just throw more hardware at it" is the root of all evil.
Svante

Re: Experience of Learning Lisp

tayssir wrote:
Like what? The only thing that bugs me is (file-position s n) instead of (setf (file-position s) n)
Take a look at:
(cons    item       collection) ; fixed :)
(gethash item       collection)
(elt     collection item)
(nth     item       collection)
(aref    collection items)
(assoc   items      collection)
(getf    collection item)
I somewhat agree and disagree with you.

I agree that nth, nth-value (which wasn't mentioned) and also gethash are reversed. I believe assoc isn't reversed because it does not belong to this group, but to the same group as find, member, remove, mapcar, ... assoc is about finding some item by walking through the list, not fetching something from a predetermined position.
(assoc item collection) ~ (find item collection :key #'car)
The same could be argued about gethash, but that is not my opinion. By the way, the thing that most confuses me is sort (and, of course, stable-sort). They should be in this "walking through" group, and as such it should be called like (sort predicate sequence) instead of (sort sequence predicate).

Since I never use nth (in these cases I either use an array or destructuring-bind, first, second, third...), I don't care about it being reversed 8-)

About cons, it shouldn't be in this group in the first place, since it constructs, not fetches, and it is not reversed for a logical reason (it is (cons first rest) because the first element comes before the rest).

And a general rule about doubts in the sequence of arguments (and what arguments are accepted): use slime and look at emacs' status bar - it never fails ;)
tayssir wrote:And there was this evil little joke stuck in 'loop':
(loop for k being the hash-keys of h using (hash-value v)
      ...)
It's maybe funny the first 100 times you type it out. (Not common enough to dedicate an emacs hotkey, not rare enough to be ignored... Maybe I should've written loop-mode.)
Hum... No arguments here :) That is one of the reasons why I like Iterate better than loop. In any case, there comes maphash for the salvation ;)
Well, not complete salvation, I agree :( but traversing hashtables is mostly useful for copying hashtables and other rare ocasions, and maphash deals with 99% of the cases very nicely.
Harleqin wrote:
tayssir wrote:A wonderful example where CL's inconsistency can trip a user up, causing a typo on the most basic operator of the bunch. ;) The art of the meta-example...
I think that this just shows that you have not understood CONS.
Well said :p
tayssir wrote:
Some of those are easily justifiable (e.g. GETF gets a place, GETHASH gets a value associcated with a hash) while others are not. It's not as incoherent as you made it to look here.
I suspect that the fact that some have easy justifications -- while others don't -- is precisely why it's 'inconsistent.' That's why we speak of inconsistency rather than (say) randomness or consistency.
I believe this is about disagreement. Disagreement sucks, there isn't much we can do about it.

CL sure does have its inconsistencies (just look at the map--- functions (how they are named) and you'll see a mess). But isn't it true in all languages? And also between different libraries - each library uses its creator's own conventions, and each one's conventions are different from each other. I believe this is a very small issue with CL, the smaller one, it is just a matter of getting used to it. The lack of standardization of modern tools (threads, sockets...), as I already mentioned, is much worse than this... Well, nothing that some compat libs like bordeaux-threads and usocket (or other socket lib) can't take care of :) but it would be much nicer and consistent if they were standardized, then you could rely on it being there always.

Re: Experience of Learning Lisp

Harleqin wrote:I think that this just shows that you have not understood CONS. Note that a list is just a shorthand for a bunch of cons cells connected in a defined way. (cons collection item) is not necessarily a mistake---unless you expected the collection to be a proper list before and after.
No, the other fellow (skypher) was correct in pointing out the typo -- he was immediately able to grasp the context, which was about constructing a proper list. But thank you for helping us stumble onto yet another inconsistency (and I hope that was your subtle point ;) ):
CL-USER> (listp (cons 1 2))
T

CL-USER> (list-length (cons 1 2))
; Evaluation aborted.
; The value 2 is not of type LIST.
;    [Condition of type TYPE-ERROR]
(Incidentally Clojure doesn't exhibit this particular behavior; (cons 1 2) throws an error, as the documentation clearly defines the 2nd parameter to be a seq.)

To use anecdotal evidence, constructing a proper list was far and away the most common use-case for CONS at the Lisp companies I worked with; IIRC, improper lists only occasionally popped up with alists. This is why I suspect my post doesn't require special explanation to most Common Lisp users. And for those which do require it, perhaps it's because of the mental confusion which CL's inconsistencies engender?

And do people use alists (where you do typically have improper lists) in contexts were hashtables are more appropriate, due to.. maybe not an 'inconsistency'.. but a conceptual incompleteness compared to other languages?

Re: Experience of Learning Lisp

tayssir wrote:But thank you for reminding me of another inconsistency (and I hope that was your subtle point ;) ):
CL-USER> (listp (cons 1 2))
T

CL-USER> (list-length (cons 1 2))
; Evaluation aborted.
; The value 2 is not of type LIST.
;    [Condition of type TYPE-ERROR]
(Incidentally Clojure doesn't exhibit this particular behavior; (cons 1 2) throws an error, as the documentation clearly defines the 2nd parameter to be a seq.)
IMO, that is not a good thing for Clojure to do. You could use cons to construct trees or pairs, not only sequences.

The function listp tests if the argument is a list, not a proper list.
tayssir wrote:And do people use alists (where you do typically have improper lists) because using hashtables is a pain, due to.. maybe not an 'inconsistency'.. but a conceptual incompleteness compared to other languages?
I disagree, with all the respect. People use alists when either the "table" is small or when you need to traverse it frequently. Hashtables are used quite often when the problem is accessing values related to keys. In other words, alist is a sequence and hashtable is a table. Walking through a hashtable is not very efficient, and I get the felling that doing it is not the right way to solve problems most times as well. That is the reason there isn't much of a need to create all sorts of hashtable functions like there are for lists or vectors. If such functions were in fact needed, there would be some library to do that.

Re: Experience of Learning Lisp

I disagree, with all the respect. People use alists when either the "table" is small or when you need to traverse it frequently. Hashtables are used quite often when the problem is accessing values related to keys. In other words, alist is a sequence and hashtable is a table. Walking through a hashtable is not very efficient, and I get the felling that doing it is not the right way to solve problems most times as well. That is the reason there isn't much of a need to create all sorts of hashtable functions like there are for lists or vectors. If such functions were in fact needed, there would be some library to do that.
[As you were responding, but before you submitted it, I edited my post with the clarification, "in contexts were hashtables are more appropriate," where 'appropriateness' is presumably decided by the lisp user's intent. (This is easily verified, since otherwise my post would automatically say it was edited.) You know, I quickly wrote that clarification because anything which appears vaguely critical about Lisp tends to be read with much more scrutiny on Lisp forums, to the extent that you have to be as pre-cise and aggressively willing to clarify misinterpretations as that fearsome Knight of the Lambda Calculus who recently passed away.]

Well, that goes to show you probably read the low-level efficiency chapter in PAIP. Congratulations, you're one of those skilled lisp programmers I keep hearing about. ;)

However, I think most people just use what's convenient, even if it compromises their intent somewhat. (Like performance similar to hashtables, and the simple ability to add/remove. (If fast performance on small tables is desired, then that optimization can be made. But people tend to worry more about the difference between constant- and linear-time performance.)

But I fear we have blundered onto yet another lisp inconsistency, far more insidious than even the preceding ones. How do people frequently construct an alist?
CL-USER> (defvar *foo*
           '((:a . 1)
             (:b . 2)))

;; “I just sensed a glitch in the Matrix.”
Ok, so they're playing with this table, and soon attempt to set the value of one of the keys via a destructive operation like (setf (cdr ...)). Like in the Hyperspec.

Oops.

It's undefined to destructively modify "literals" like quoted lists. Missiles may well be on their way to your home. (As mentioned in another thread, Lisp is a DARPA technology, so this is likelier than we might expect.)

People have been bewildered after falling prey to this problem. Real-world implementations differ on the semantics: LispWorks did one thing and SBCL another.

Re: Experience of Learning Lisp

tayssir wrote:
CL-USER> (listp (cons 1 2))
T

CL-USER> (list-length (cons 1 2))
; Evaluation aborted.
; The value 2 is not of type LIST.
;    [Condition of type TYPE-ERROR]
What's inconsistent about not being able to take the length of improper lists? I don't even know why a person would try to do this intentionally, since there would be no way of guessing how the standard defined their length short of looking it up.

And of course Common Lisp doesn't error on (cons 1 2). Representing pairs is a perfectly valid use for cons cells. Not that some other functions aren't dismayingly inconsistent, but I don't see how either cons or list-length is strange.

Re: Experience of Learning Lisp

tayssir wrote:
Like what? The only thing that bugs me is (file-position s n) instead of (setf (file-position s) n)
Take a look at:
(cons    collection item)
(gethash item       collection)
(elt     collection item)
(nth     item       collection)
(aref    collection items)
(assoc   items      collection)
(getf    collection item)
Other than your use of CONS to build a list along the CARs, I don't see the problem. The only functions in your list that could sensibly go either way are GETHASH and GETF, but it makes sense that they're the way they are—GETHASH is consistent with ASSOC; GETF follows GET for obvious reasons (which made sense at the time; less so now)

Re: Experience of Learning Lisp

tayssir wrote:(Incidentally Clojure doesn't exhibit this particular behavior; (cons 1 2) throws an error, as the documentation clearly defines the 2nd parameter to be a seq.)
:roll:

Another reason to avoid Clojure.

Re: Experience of Learning Lisp

tayssir wrote:But I fear we have blundered onto yet another lisp inconsistency, far more insidious than even the preceding ones. How do people frequently construct an alist?
CL-USER> (defvar *foo*
           '((:a . 1)
             (:b . 2)))

;; “I just sensed a glitch in the Matrix.”
...
It's undefined to destructively modify "literals" like quoted lists.
Judging by the prompt in front, your "literal" was defined at the REPL, so it's perfectly well defined, but in any case, how would that be "an inconsistency"?

Re: Experience of Learning Lisp

tayssir wrote:
CL-USER> (defvar *foo*
           '((:a . 1)
             (:b . 2)))

;; “I just sensed a glitch in the Matrix.”
Ok, so they're playing with this table, and soon attempt to set the value of one of the keys via a destructive operation like (setf (cdr ...)). Like in the Hyperspec.
Hum... That really sucks...

I believe this is user's mistake, not implementation's nor CL's one. This is constant data, and modifying constant data would be an error in any language (other than Clojure, you might say, and perhaps a few other languages which use non-destructive data, but that is a Clojure's feature, not CL's defect).

In any case, thanks for the advice, I'll remember to always use copy-tree when I create alists like this one.

Re: Experience of Learning Lisp

gugamilare wrote:In any case, thanks for the advice, I'll remember to always use copy-tree when I create alists like this one.
Or even better just build your list with LIST unless you can guarantee that it won't be modified by you or someone else using that list.

Re: Experience of Learning Lisp

tayssir wrote:
CL-USER> (defvar *foo*
           '((:a . 1)
             (:b . 2)))
Please, this is well known stuff for most programmers and if not it is an useful feature to be (made) aware of. Literal data, constants and SBCL's global variables (SB-EXT:DEFGLOBAL) are important when things need to execute as fast as possible.

Always-bound globals that cannot (by user's wish; they specify intention) be locally bound are of interest for their semantics also; not just performance.

edit:
Just in case, before getting hit with "you shouldn't optimize prematurely" or similar -- you should know that there are people out there who deal with other problems and things than you do. These people very much welcome the fact that CL is multi-paradigm and has a core language for them to build on with almost no indirection in it by default.

Re: Experience of Learning Lisp

Before I spend more of my time answering further, no one responded to this claim:
Paul wrote:
tayssir wrote:
CL-USER> (defvar *foo*
           '((:a . 1)
             (:b . 2)))
Ok, so they're playing with this table, and soon attempt to set the value of one of the keys via a destructive operation like (setf (cdr ...)). Like in the Hyperspec.

Oops.
Judging by the prompt in front, your "literal" was defined at the REPL, so it's perfectly well defined, but in any case, how would that be "an inconsistency"?
What do people think: is this refutation correct or not? If the DEFVAR was executed at the repl, is the subsequent destructive operation no longer undefined by the spec? (To start you off, here is this hyperspec entry.)

Re: Experience of Learning Lisp

tayssir wrote:Before I spend more of my time answering further, no one responded to this claim:
Paul wrote:
tayssir wrote:
CL-USER> (defvar *foo*
           '((:a . 1)
             (:b . 2)))
Ok, so they're playing with this table, and soon attempt to set the value of one of the keys via a destructive operation like (setf (cdr ...)). Like in the Hyperspec.

Oops.
Judging by the prompt in front, your "literal" was defined at the REPL, so it's perfectly well defined, but in any case, how would that be "an inconsistency"?
What do people think: is this refutation correct or not? If the DEFVAR was executed at the repl, is the subsequent destructive operation no longer undefined by the spec? (To start you off, here is this hyperspec entry.)
The reader must generate a new list when it reads the form in, and QUOTE just returns the identical list that is its argument, therefore it returns the fresh list constructed by the reader; there's no way to tell the difference between that list and one constructed with LIST, therefore there's no way mutating it can have any different effect than if you had used LIST. So either all list mutations are undefined, or this one isn't (or Lisp is magical).

The spec is allowing for the possibility of similar lists being merged when externalized in a FASL file, etc.

Re: Experience of Learning Lisp

Paul wrote:The reader must generate a new list when it reads the form in, and QUOTE just returns the identical list that is its argument, therefore it returns the fresh list constructed by the reader; there's no way to tell the difference between that list and one constructed with LIST, therefore there's no way mutating it can have any different effect than if you had used LIST. So either all list mutations are undefined, or this one isn't (or Lisp is magical).

The spec is allowing for the possibility of similar lists being merged when externalized in a FASL file, etc.
The problem is that modifying literal data may break something. This example is classic:
(defun foo ()
  '(1 2 3))

(setf (car (foo)) 10)

(foo) => (10 2 3)
Anyway, in the given example, I don't know any way to break that code, but the same example using a local variable inside a function, for instance, will break the code (I once saw a friend of mine struggling to find where was the bug in his function because of this, and I explained to him that he couldn't modify constant data since it is allocated only once during compilation or load time).

Re: Experience of Learning Lisp

Paul wrote:
tayssir wrote:
CL-USER> (defvar *foo*
           '((:a . 1)
             (:b . 2)))

;; “I just sensed a glitch in the Matrix.”
...
It's undefined to destructively modify "literals" like quoted lists.
Judging by the prompt in front, your "literal" was defined at the REPL, so it's perfectly well defined, but in any case, how would that be "an inconsistency"?
The spec contradicts this claim, and Kent Pitman (hyperspec author extraordinare) cites fairly nasty consequences — even at the REPL.

This common misconception is instructive: we see inconsistencies in CL's spec even where there are none. (In this case, special-casing of the REPL.) This is not unlike the situation that many of us have probably been in (I hope I'm not the only one), where we're wrestling with some difficult-to-install library, and come to blame the library even in particular moments when we happen to be at fault.

Note that Kent was replying to a lisp implementor (Roger Corman) who just goofed up on this point. (So there's no shame in that.) Elsewhere in the thread, you'll find another implementor (Duane Rettig of Franz) share his own war-stories of discovering that Franz software had similar bugs due to modifying literals, which he claimed nearly led to more years of hard-to-reproduce bugs.

The reader must generate a new list when it reads the form in, and QUOTE just returns the identical list that is its argument, therefore it returns the fresh list constructed by the reader; there's no way to tell the difference between that list and one constructed with LIST, therefore there's no way mutating it can have any different effect than if you had used LIST. So either all list mutations are undefined, or this one isn't (or Lisp is magical).
As Kent cited, simple optimizations can defeat that, REPL or no. Implementations like MCL can decide to share quoted literals throughout the program, put them in read-only spaces, etc.

And if Kent and the spec are wrong, the resulting inconsistency would only provide more lurid evidence of CL's inconsistency.

Re: Experience of Learning Lisp

tayssir wrote:This common misconception is instructive: we see inconsistencies in CL's spec even where there are none. (In this case, special-casing of the REPL.) This is not unlike the situation that many of us have probably been in (I hope I'm not the only one), where we're wrestling with some difficult-to-install library, and come to blame the library even in particular moments when we happen to be at fault.

[...] goofed up [...] war-stories [...] bugs due to modifying literals, which he claimed nearly led to more years of hard-to-reproduce bugs.

[...]

And if Kent and the spec are wrong, the resulting inconsistency would only provide more lurid evidence of CL's inconsistency.
Sorry, but I think this is all impractical nitpicking (I meant to put in some harsher phrase here but reconsidered).

I've been programming in C, C++, Java, PHP and Perl for years before starting with Common Lisp.
Now in all the years I spent with CL I have found it to provide an extreme productivity boost, shortening the path between thought and tangible product considerably. Hardly have I ever found literals to impede this, and they certainly never caused visible bugs in libraries or the implementations I've been using.

I suspect this is because most sane Lisp programmers follow some simple rules, like:

* don't use destructive functions unless you need to; if you do, be careful and double-check.
* don't use literal lists until you're absolutely sure what you're doing.

Note that the first thing is imperative even if dealing with non-literal lists. Once you get destructive you're much more liable to mess up. This is not a CL problem.
As Kent cited, simple optimizations can defeat that, REPL or no. Implementations like MCL can decide to share quoted literals throughout the program, put them in read-only spaces, etc.
That's right.


This discussion now went the way a lot of discussions on comp.lang.lisp go: some CL newbie is asking innocent questions that are answered well. Then some more or less disgruntled guy comes along, picks one minor issue in CL and leads the whole topic astray, making people discuss obscure corners of the spec. This makes me incredibly sad because it makes the thread pretty much worthless for the OP from the point on, and I hope the Lispforum community will not continue to follow this path.

Please, please at least start your own topic instead of hijacking more productive threads if you want to argue about stuff like this.

Leslie

Re: Experience of Learning Lisp

tayssir wrote:The spec contradicts this claim,
It really doesn't.
tayssir wrote:and Kent Pitman (hyperspec author extraordinare) cites fairly nasty consequences — even at the REPL.
Haha. Kent's wrong on this one! Calling compile doesn't make any difference—compile can't change the identity of its data.
tayssir wrote:As Kent cited, simple optimizations can defeat that, REPL or no. Implementations like MCL can decide to share quoted literals throughout the program, put them in read-only spaces, etc.
It can't, because it can't identify them. Any implementation can coalesce externalized data (in the implementation binary and/or FASL files), but it can't change the identity of any data in core.
tayssir wrote:And if Kent and the spec are wrong, the resulting inconsistency would only provide more lurid evidence of CL's inconsistency.
Inconsistency with what? It's perfectly consistent with the definitions of all the functions, special operators, and macros in use. It would inconsistent only if it behaved differently for read-time values than run-time values.

Re: Experience of Learning Lisp

Sorry, but I think this is all impractical nitpicking (I meant to put in some harsher phrase here but reconsidered).
Good thing you spare us the pejoratives, as you may otherwise be moderated, and I'd rather you not be. This alternative passive-aggressive path (telling me about the flames you wish to roast me with, without actually daring to say them) is fine by me. ;)

Sometimes I prefer to make concrete references to code and the hyperspec, as these are the meat & potatoes things which real lisp programmers must employ. This is a necessary counterweight to the necessarily more vague philosophical discussions, since anyone can say anything if not confronted by the hyperspec, lisp output and reality.

This discussion now went the way a lot of discussions on comp.lang.lisp go: some CL newbie is asking innocent questions that are answered well.
Excuse me? The original poster enthusiastically talks about rewriting lisp, because "It seems cruel to me that the amazing power of Lisp is hidden away in a cryptic language riddled with idiosyncrasies and inconsistencies." This so-called 'innocent newbie' can perceive beauty through wild cryptic idiosyncracy.

That post sounded cool, and the author happens to be part of my intended audience. You're not.

Then some more or less disgruntled guy comes along, picks one minor issue in CL and leads the whole topic astray, making people discuss obscure corners of the spec.
You must be aware, this is a curious claim. Did I just make you publicly go offtopic and post personally uncivil claims which have nothing to do with technical issues? I apologize for having that power over you.

But I'm really quite gruntled! On this very topic, I advocated my two fave languages (CL and one of its direct descendants).

Yes, you know, admittedly I get a bit sick when I see the whole groupthink which sometimes occurs on lisp hangouts (especially when phrases like "get used to it" creep into the discussion). This is admittedly an emotional motivation on my part, and if it really bothers you, I'll try to tone it down.

That said, the flipside of advocacy is honesty. If you don't "police your own" (in a nice way) when they make factual errors, or you paint an absurdly rosy picture of your tools, I consider that less than honest and a disservice to interested newcomers.

Re: Experience of Learning Lisp

If the discussion wasn't off-topic, it is going to that direction now. Please, gentlemen, calm down.

I agree with Paul when he says this is not an "inconsistency", it does not fit the definition of the word itself. At most it is a small defect of the language (like any language has) which causes a few headaches when you find it, but not more than, for instance, the headache you get when you still don't know that floating-points are only the approximate value you give in decimal because it is converted to binary, and that may lead to some mathematical mistakes (for instance, when you handle money). You may even end up losing a few pennies ;)

I disagree with tayssir when he implies that "getting used to" is a bad thing. There are always things you need to get used to, in any language, because that is what we trade for efficiency. For instance, if you don't need to get used to modifying constant data in Clojure it is because it will make a fresh copy of everything it needs to modify, and that will certainly create more trash and require more memory. I'm not criticising Clojure for this behaviour, since this particular behaviour will make programs safer. I'm just saying it is a matter of choice.

Finally I agree with tayssir that this behaviour is not a very good thing. Even if there is no problem using quoted lists in defvars, there still is a problem to bind quoted lists in local variables and modifying it, so his point is still valid. But he is making of this more of an issue than it really is, this is really no big deal, because every mature programmer knows about that and that is easily avoidable.

Anyway, that should be my last post about this, I think I don't have any constructive point yet to be made.

Re: Experience of Learning Lisp

Clojure does "clean up" some things that are inconsistent or arcane in CL in my opinion. Clojure has only one implementation (so far) and so there are very few holes where things "vary by implementation", which makes sharing code a whole lot easier. I think some of the naming conventions are better; you don't have to remember if it's SOMETHINGP or SOME-THING-P, it's always "something?". Unifying mapping and iteration and access functions for lists/vectors/hashmaps/sets under one common interface simplifies a lot of things. Built-in syntax for {:hash 'maps}, #(sets), [vectors], #(anonymous functions), and #"regexes" brings me no end of glee. Case-sensitivity of symbol names is a boon. There are few mutable data structures in Clojure, so you don't have to worry about destructive vs. non-destructive functions. Paring away some unneeded parens in various places was a good thing. Being able to drop some of the historic vocabulary (CAR, CDR, LAMBDA) and some of the lengthy yet cryptic function names (DESTRUCTURING-BIND, ENOUGH-NAMESTRING) in favor of shorter and simpler names was a good thing. All just my opinion of course.

However Clojure has stumbling blocks of its own. It has the same "everything dumped into one package" for its built-in functions arrangement that CL has (with very few exceptions, like clojure.set and clojure.xml). Some names are abbreviated but it's not obvious what the abbreviation means. What does "comp" do? Complement? Compose? Compare? (It's "compose".) There's repeat vs. replicate vs. repeatedly, pr vs. prn vs. print vs. println, cons vs. conj, and so on. You have (map f coll) but (nth coll n), (conj coll x) but (cons x coll).

Or for an example vaguely reminiscent of CL's funky behavior with literal lists: a literal hash-map in Clojure is made an array-map by default if the length is short, for performance reasons. But this leads to odd behavior if you have duplicate keys.
user> (def x {:foo 'bar :foo 'huh?})
#'user/x
user> x
{:foo bar, :foo huh?}
user> (count x)
2
user> (:foo x)
bar
I've seen people complain that given a function that you know must exist, tracking down its name can be difficult in Clojure. There's find-doc in Clojure and APROPOS in CL but it's still a guessing game sometimes. Another quirk common to CL and Clojure is remembering whether something is a macro or a function, and whether it needs its arguments quoted or not. This used to trip me up a lot at first.

But no language is free from these kinds of inconsistencies. I clearly remember having these same problems and complaints when learning Python and Ruby and Java. And Perl, which is pretty much nothing but inconsistencies. All of these problems go away to some degree with time and practice and the unpleasant grunt-work of reading APIs and memorizing things.

I used to make fun of Java because you can't really write good Java code without an IDE to support you; it's too verbose to type without typos and remembering the name of everything is painful. But it's almost true for Lisps too that you really need a good editor to write Lisp code effectively. First and foremost to keep your parens balanced for you a la paredit, and secondly to let you quickly access documentation. You don't really need to remember the order of arguments of a function when Emacs is going to remind you of that function's signature every time you start to type it.

If you rewrote CL to clean it up, you could get a long way to making it look like something present-day programmers are more familiar and comfortable with, but it still wouldn't be perfect and it would still require a bunch of fumbling and memorization at first just like any other language.

Re: Experience of Learning Lisp

Unne wrote:I think some of the naming conventions are better; you don't have to remember if it's SOMETHINGP or SOME-THING-P, it's always "something?".
So you just have to remember to use a question mark to denote statements of fact. :? And have to disambiguate it when speaking, unless you consider spoken Lisp to be a tonal language.

Sorry, that naming convention just drives me batty. IMO, marking predicates with a “p” makes a heck of a lot more sense.

Re: Experience of Learning Lisp

gugamilare wrote:[...] Anyway, that should be my last post about this, I think I don't have any constructive point yet to be made.
Well, at least you did a better job than me in explaining the point I originally intended to convey.

So thanks, and sorry everyone if my own post weighed in too much ad hominem, this wasn't intentional.

Re: Experience of Learning Lisp

Paul Donnelly wrote:So you just have to remember to use a question mark to denote statements of fact. :? And have to disambiguate it when speaking, unless you consider spoken Lisp to be a tonal language.

Sorry, that naming convention just drives me batty. IMO, marking predicates with a “p” makes a heck of a lot more sense.
I don't really see them as statements of fact, I see them as questions. "Is this a hash table?" is the question, and the answer is true or false. But I don't know, which is better, "hash hyphen table hyphen pee" or "hash hyphen table question mark"? They're both pretty terrible to say aloud. You could say "hash table predicate" and everyone would just know there's a question mark on the end, probably. Schemers have been using ? for a long time, they must have some conventions. Tonal languages are fun though. Japanese Lispers would be happy. :D

I don't think programming languages are really meant to be spoken aloud. Especially Lisp. How do you communicate parentheses? I could envision a rudimentary form of sign language.

Re: Experience of Learning Lisp

Paul Donnelly wrote:
Unne wrote:I think some of the naming conventions are better; you don't have to remember if it's SOMETHINGP or SOME-THING-P, it's always "something?".
So you just have to remember to use a question mark to denote statements of fact. :? And have to disambiguate it when speaking, unless you consider spoken Lisp to be a tonal language.

Sorry, that naming convention just drives me batty. IMO, marking predicates with a “p” makes a heck of a lot more sense.
I haven't said much and don't care much on the general conversation, but I have to disagree with you on this one. When I first saw that convention in Scheme, it made instant sense to me. The "-p" convention only makes sense once you figure out that it stands for "predicate," and you happen to know what a predicate is, which many programmers won't.

Ditto with using a prefix "n" for non-consing mutable routines. It took me a couple months of working with CL before I found a reference that described that. I still think Scheme's "!" notation makes more sense.

That said, this is all just personal preference. Either way we're talking about arbitrary character sequences. The just happen to tickle each of our brains in a slightly different way.
Cheers, Dave
Slowly but surely the world is finding Lisp. http://www.findinglisp.com/blog/

Re: Experience of Learning Lisp

But he is making of this more of an issue than it really is, this is really no big deal, because every mature programmer knows about that and that is easily avoidable.
Maybe. I'm surely putting Lisp under a microscope. You may see more bizarre things if you do this with an organism rather than a brick. And that's the point.

"Pascal is for building pyramids — imposing, breathtaking structures built by armies pushing heavy blocks into place. Lisp is for building organisms..." ― Alan Perlis

Currently, I use lisp to generate code in other languages, avoiding horrendous errors like SQL injection attacks. When I took a hacking course (yes, such things exist), they demonstrated that PHP coders are supposed to do a lot of tedious text escaping. Clearly, Lisp users avoid such problems because they're probably using some easy-to-use tool which Just Takes Care of It. I spent part of a weekend writing a little lisp tool which generated PHP/SQL automatically, with appropriate input validation and type inferencing. The code which came out of it was solid.

(Maybe I should slap a LispWorks GUI on it and make it available as a public service.)

Nowadays, I see so much public-facing code with SQL injection vulnerabilities that I don't bother to fix them. I simply point it out to whomever has decisionmaking ability, with the knowledge that it won't be fixed, and move on to the next zillion tasks. (But I don't "get used to it," like I don't get used to quoted literals' undefinedness, or how you have to remember to set *READ-EVAL*. Something in the back of my mind still voices annoyance at statistically trading correctness for.. whatever benefit which could be obtained in a better way anyway.. and I'm reminded that I still look at programming as an outsider.)

So, that is how I might put other languages in perspective, unmagnified. I use Lisp to correct deficiencies of other languages.

(But that said, I've seen terribly undebuggable Real World ™ code in Lisp. In certain dimensions, it may be the worst code I've ever read. Ultimately, the good thing about CL — it's a Big Ball of Mud — can also be the downfall of some poor codemonkey blinking at perverse uses of DECLARE SPECIAL. ;) )

Re: Experience of Learning Lisp

Unne wrote:
Paul Donnelly wrote:So you just have to remember to use a question mark to denote statements of fact. :? And have to disambiguate it when speaking, unless you consider spoken Lisp to be a tonal language.

Sorry, that naming convention just drives me batty. IMO, marking predicates with a “p” makes a heck of a lot more sense.
I don't really see them as statements of fact, I see them as questions. "Is this a hash table?" is the question, and the answer is true or false. But I don't know, which is better, "hash hyphen table hyphen pee" or "hash hyphen table question mark"? They're both pretty terrible to say aloud. You could say "hash table predicate" and everyone would just know there's a question mark on the end,
I read it as "hash table pee"; I don't pronounce punctuation; "hash-table?" is just pronounced "hash table" with rising intonation, and wants a pause after it; it's quite difficult to read - or understand - things like "(hash-table? x)" because that's two sentences, not one.
Tonal languages are fun though. Japanese Lispers would be happy. :D
But Japanese isn't a tonal language...pitch accent is not the same thing.

Re: Experience of Learning Lisp

Unne wrote:I don't really see them as statements of fact, I see them as questions. "Is this a hash table?" is the question, and the answer is true or false.
But that's exactly the problem. “Is this a hash table,” is a question, and can't possiblly be true or false. To be true or false, you have to be making an assertion. If it returned #y or #n, I'd have no objection. You wouldn't give points for a yes/no answer on a true/false test, would you?
findinglisp wrote:I haven't said much and don't care much on the general conversation, but I have to disagree with you on this one. When I first saw that convention in Scheme, it made instant sense to me. The "-p" convention only makes sense once you figure out that it stands for "predicate," and you happen to know what a predicate is, which many programmers won't.
Programmers with no exposure to logic! :shock: Then let's teach them logic. :ugeek:
findinglisp wrote:Ditto with using a prefix "n" for non-consing mutable routines. It took me a couple months of working with CL before I found a reference that described that. I still think Scheme's "!" notation makes more sense.
I agree. And at least “!” can be pronounced “bang”, which is no worse to say than “n”.

Re: Experience of Learning Lisp

Paul wrote:But Japanese isn't a tonal language...pitch accent is not the same thing.
Sorry, you're right, I wasn't familiar with what "tonal language" meant. Informal Japanese uses intonation to denote whether something is a question or a statement, which is why I thought of it here.

Last edited by Unne on , edited 1 time in total.

Re: Experience of Learning Lisp

mrove wrote: How long did it take you to get started?
Dunno quite what you mean by that. In my second semester of Uni I took a class taught in scheme. I learned about as much scheme as I had to to do well in the class. The next semester I took an AI class taught in CL ( yes, I am an old man- that class is almost certainly taught in Java now), so I had to learn enough CL to nail the programming assignments. My basic strategy as an undergrad was to ignore everything other than the programming assignments and the exams, and figure the curve would fix things for me. That worked pretty well as long as I really did well on those. So I learned enough CL to do well on them.

I think that having pretty tough assignments can be helpful. Pretty close to the first thing I ever wrote in scheme was a reasonably general implementation of Dijkstra's painting algorithm. A few weeks after that I had to write a translator from a subset of Pascal to Scheme. I'd say that that was where I first started to get it- we were provided with a framework that made it clear that we had to make specific parsers by returning parsing functions from functions that made parsers based on their parameters. Very few people in that class actually understood the assignment.

For a couple of years after I left school I programmed in a variety of languages that could be best characterized by saying that none were actually Lisp. I did a fair bit of web programming in languages like Perl and Python. I also wound up getting sucked into a number of disastrous projects written in C, mainly because I was a pretty good C programmer at the time. I also got sucked into a few really disastrous projects written in C++, mainly because I was a pretty good C programmer- well, I eventually learned a bit of C++, but I also learned that being a good C programmer has almost nothing to do with being a good C++ programmer.

To tell you the truth I was about ready to stop programming just a few years after having spent four years and a lot of money studying CS at Uni. It seemed like every project I worked on was doomed to fail. But I started thinking about why the projects I was hired to work on were doomed to fail, and.. well choice of programming language was not the main factor. But- it was a factor. I kept thinking about what was wrong with the projects, and I kept remembering CL. Every time I had to go to lunch in order to let the project compile, I started thinking about Lisp.

So I guess that by the time I started programming mainly in CL I had already got it- that is, I knew what I wanted from it. On the other hand I feel like I am still getting it. I'm not a bad CL programmer, but there are a lot of really good CL programmers out there. I mean- the best CL programmers are pretty decent programmers by any measure. So I feel like I am still getting it... then again I feel the same way about Java. I think Java is vastly ionferior to CL, but I have to program in it to make a living. I learn a lot from great Java programmers, even if I think Java is basically flawed.

Anyway, you'll get started on the day that you write something in CL, as long as you write more the next day. The only way I can think of to not get started is to not write code. Of course you coulf ask: "When did you really get it?" But that might not be that useful, tbh.

Re: Experience of Learning Lisp

Paul,

I still don't get why (defvar *list* '(1 2 3)) at the REPL could not in any way be stuffed into read-only space by the implementation...

Re: Experience of Learning Lisp

skypher wrote:Paul,

I still don't get why (defvar *list* '(1 2 3)) at the REPL could not in any way be stuffed into read-only space by the implementation...
At what point could it be stuffed into read-only space?

Re: Experience of Learning Lisp

Paul wrote:
skypher wrote:Paul,

I still don't get why (defvar *list* '(1 2 3)) at the REPL could not in any way be stuffed into read-only space by the implementation...
At what point could it be stuffed into read-only space?
Well, this will be expanded into:
(defvar *list* (quote (1 2 3)))
Then the compiler (or evaluator) read the "quote" special operator and move the list (1 2 3) to read-only space.

Re: Experience of Learning Lisp

gugamilare wrote:
Paul wrote:
skypher wrote:Paul,

I still don't get why (defvar *list* '(1 2 3)) at the REPL could not in any way be stuffed into read-only space by the implementation...
At what point could it be stuffed into read-only space?
Well, this will be expanded into:
(defvar *list* (quote (1 2 3)))
Then the compiler (or evaluator) read the "quote" special operator and move the list (1 2 3) to read-only space.
And if I type
(defvar *foo* (list 1 2 3))

(defvar *list* (quote #.*foo*))
the QUOTE can't distinguish between what it sees now and what it saw in your example, so it moves my *foo* list into read-only space....and fails to comply with the standard!

Re: Experience of Learning Lisp

Paul wrote:And if I type
(defvar *foo* (list 1 2 3))

(defvar *list* (quote #.*foo*))
the QUOTE can't distinguish between what it sees now and what it saw in your example, so it moves my *foo* list into read-only space....and fails to comply with the standard!
Yes, you are probably right that no one would do this. As a hypothetical example, though, the implementation could have added some tags to the value of *foo* when expanding #.*foo*, and quote would see that tag and wouldn't move it into read-only space.

Re: Experience of Learning Lisp

Perhaps this is not a so hypothetical example as I said. It looks like many implementations do (or did) collapse their constant data when compiling files.

I found something interesting at this X3J13 issue.

According to quote in hyperspec, it looks like your example is unportable code, since the spec clearly says:
The consequences are undefined if literal objects (including quoted objects) are destructively modified.
You code is quoted, therefore it is considered to be literal, even though it comes from non-constant data. The spec also states that compile-file is allowed to coalesce or copy constant data. The same is not true to eval or compile, but compile-file could be used to implement the REPL, therefore potentially bringing those issues to the REPL, but now this is a hypothetical example, since it is stupid not to use compile or eval instead. And, even though the specification restricted the copying and coalescing of data by the compile function, Kent Pitman said that at least MCL coalesced data when compiling code. But that was back in 2001, now there is no MCL anymore, but I think this justifies what he was trying to say.

Anyway, as I usually think, the REPL is an informal tool, you can and should do with it whatever you want to do without any harm with an implementation you trust (for instance, SBCL, which would warn you when you are doing something potentially dangerous). If something goes wrong, you can find the error, learn from it and try again in a different way or report it. But when writing files which you will distribute, you should to be careful with constant data.

Re: Experience of Learning Lisp

gugamilare wrote:
Paul wrote:And if I type
(defvar *foo* (list 1 2 3))

(defvar *list* (quote #.*foo*))
the QUOTE can't distinguish between what it sees now and what it saw in your example, so it moves my *foo* list into read-only space....and fails to comply with the standard!
Yes, you are probably right that no one would do this. As a hypothetical example, though, the implementation could have added some tags to the value of *foo* when expanding #.*foo*, and quote would see that tag and wouldn't move it into read-only space.
Then (EVAL (LIST (QUOTE QUOTE) *FOO*) will screw it up...etc. Anyway, the issue has nothing whatsoever to do with QUOTE: you can type most literals without QUOTE (e.g., strings, vectors, pathnames, ...)

Re: Experience of Learning Lisp

gugamilare wrote:Perhaps this is not a so hypothetical example as I said. It looks like many implementations do (or did) collapse their constant data when compiling files.
Yes; compile-file is a different issue (I already mentioned that).
but compile-file could be used to implement the REPL
No it can't. That would destroy object identity.