I have read that most languages are becoming more and more like lisp, adopting features that lisp has had for a long time. I was wondering, what are the features, old or new, that lisp does not have? By lisp I mean the most common dialects like Common Lisp and Scheme.
Decent syntax. (Someone had to say it.) It may be simple/uniform/homoiconic/macro-able/etc, but as a human, I just loathe looking at it :)
- It can be harder than in more popular languages to find good libraries.
- It is not purely functional like haskell
This question has been asked a million times, but here goes. Common Lisp was created at a time when humans were considered cheap, and machines were considered expensive. Common Lisp made things easier for humans at the expense of making it harder for computers. Lisp machines were expensive; PCs with DOS were cheap. This was not good for its popularity; better to get a few more humans making mistakes with less expressive languages than it was to buy a better computer.
Fast forward 30 years, and it turns out that this isn't true. Humans are very, very expensive (and in very short supply; try hiring a programmer), and computers are very, very cheap. Cheaper than dirt, even. What today's world needs is exactly what Common Lisp offered; if Lisp were invented now, it would become very popular. Since it's 30-year-old (plus!) technology, though, nobody thought to look at it, and instead created their own languages with similar concepts. Those are the ones you're using today. (Java + garbage collection is one of the big innovations. For years, GC was looked down upon for being "too slow", but of course, a little research and now it's faster than managing your own memory. And easier for humans, too. How times change...)
- Pass-by-reference (C++/C#)
- String interpolation (Perl/Ruby)
- Nice infix syntax (though it's not clear that it's worth it) (Python)
- Monadic 'iteration' construct which can be overloaded for other uses (Haskell/C#/F#/Scala)
- Static typing (though it's not clear that it's worth it) (many languages)
- Type inference (not in the standard at least) (Caml and many others)
- Abstract Data Types (Haskell/F#/Caml)
- Pattern matching (Haskell/F#/Caml/Scala/others)
- Backtracking (though it's not clear that it's worth it) (Prolog)
ad-hoc polymorphism(see Andrew Myers' answer)- immutable data structures (many languages)
- lazy evaluation (Haskell)
(Please add to this list, I have marked it community wiki.)
This just refers to the Common Lisp and Scheme standards, because particular implementations have added a lot of these features independently. In fact, the question is kind of mistaken. It's so easy to add features to Lisp that it's better to have a core language without many features. That way, people can customize their language to perfectly fit their needs.
Of course, some implementations package the core Lisp with a bunch of these features as libraries. At least for Scheme, PLT Scheme provides all of the above features*, mostly as libraries. I don't know of an equivalent for Common Lisp, but there may be one.
*Maybe not infix syntax? I'm not sure, I never looked for it.
This is in response to the discussion in comments under Nathan Sanders reply. This is a bit much for a comment so I'm adding it here. I hope this isn't violating Stackoverflow etiquette.
ad-hoc polymorphism is defined as different implementations based on specified types. In Common Lisp using generic methods you can define something like the following which gives you exactly that.
;This is unnecessary and created implicitly if not defined.
;It can be explicitly provided to define an interface.
(defgeneric what-am-i? (thing))
;Provide implementation that works for any type.
(defmethod what-am-i? (thing)
(format t "My value is ~a~%" thing))
;Specialize on thing being an integer.
(defmethod what-am-i? ((thing integer))
(format t "I am an integer!~%")
(call-next-method))
;Specialize on thing being a string.
(defmethod what-am-i? ((thing string))
(format t "I am a string!~%")
(call-next-method))
CL-USER> (what-am-i? 25)
I am an integer!
My value is 25
NIL
CL-USER> (what-am-i? "Andrew")
I am a string!
My value is Andrew
NIL
- Whole-program transformations. (It would be just like macros, but for everything. You could use it to implement declarative language features.) Equivalently, the ability to write add-ons to the compiler. (At least, Scheme is missing this. CL may not be.)
- Built-in theorem assistant / proof checker for proving assertions about your program.
Of course, I don't know of any other language that has these, so I don't think there's much competition in terms of features.
For Common Lisp, I think the following features would be worth adding to a future standard, in the ridiculously unlikely hypothetical situation that another standard is produced. All of these are things that are provided by pretty much every actively maintained CL implementation in subtly incompatible ways, or exist in widely used and portable libraries, so having a standard would provide significant benefits to users while not making life unduly difficult for implementors.
Some features for working with an underlying OS, like invoking other programs or handling command line arguments. Every implementation of CL I've used has something like this, and all of them are pretty similar.
Underlying macros or special forms for
BACKQUOTE
,UNQUOTE
andUNQUOTE-SPLICING
.The meta-object protocol for CLOS.
A protocol for user-defined
LOOP
clauses. There are some other waysLOOP
could be enhanced that probably wouldn't be too painful, either, like clauses to bind multiple values, or iterate over a generic sequence (instead of requiring different clauses forLIST
s andVECTOR
s).A system-definition facility that integrates with
PROVIDE
andREQUIRE
, while undeprecatingPROVIDE
andREQUIRE
.Better and more extensible stream facilities, allowing users to define their own stream classes. This might be a bit more painful because there are two competing proposals out there, Gray streams and "simple streams", both of which are implemented by some CL implementations.
Better support for "environments", as described in CLTL2.
A declaration for merging tail calls and a description of the situations where calls that look like tail calls aren't (because of
UNWIND-PROTECT
forms,DYNAMIC-EXTENT
declarations, special variable bindings, et c.).Undeprecate
REMOVE-IF-NOT
and friends. Eliminate the:TEST-NOT
keyword argument andSET
.Weak references and weak hash tables.
User-provided hash-table tests.
PARSE-FLOAT
. Currently if you want to turn a string into a floating point number, you either have to useREAD
(which may do all sorts of things you don't want) or roll your own parsing function. This is silly.
Here are some more ambitious features that I still think would be worthwhile.
A protocol for defining sequence classes that will work with the standard generic sequence functions (like
MAP
,REMOVE
and friends). Adding immutable strings and conses alongside their mutable kin might be nice, too.Provide a richer set of associative array/"map" data types. Right now we have ad-hoc stuff built out of conses (alists and plists) and hash-tables, but no balanced binary trees. Provide generic sequence functions to work with these.
Fix
DEFCONSTANT
so it does something less useless.Better control of the reader. It's a very powerful tool, but it has to be used very carefully to avoid doing things like interning new symbols. Also, it would be nice if there were better ways to manage readtables and custom reader syntaxes.
A read syntax for "raw strings", similar to what Python offers.
Some more options for CLOS classes and slots, allowing for more optimizations and better performance. Some examples are "primary" classes (where you can only have one "primary class" in a class's list of superclasses), "sealed" generic functions (so you can't add more methods to them, allowing the compiler to make a lot more assumptions about them) and slots that are guaranteed to be bound.
Thread support. Most implementations either support SMP now or will support it in the near future.
Nail down more of the pathname behavior. There are a lot of gratuitously annoying incompatibilities between implementations, like CLISP's insistance on signaling an error when you use
PROBE-FILE
on a directory, or indeed the fact that there's no standard function that tells you whether a pathname is the name of a directory or not.Support for network sockets.
A common foreign function interface. It would be unavoidably lowest-common-denominator, but I think having something you could portably rely upon would be a real advantage even if using some of the cooler things some implementations provide would still be relegated to the realm of extensions.
You are asking the ronge question. The language with the most features isnt the best. A language needs a goal.
We could add all of this and more
* Pass-by-reference (C++/C#)
* String interpolation (Perl/Ruby)
* Nice infix syntax (though it's not clear that it's worth it) (Python)
* Monadic 'iteration' construct which can be overloaded for other uses (Haskell/C#/F#/Scala)
* Static typing (though it's not clear that it's worth it) (many languages)
* Type inference (not in the standard at least) (Caml and many others)
* Abstract Data Types (Haskell/F#/Caml)
* Pattern matching (Haskell/F#/Caml/Scala/others)
* Backtracking (though it's not clear that it's worth it) (Prolog)
* ad-hoc polymorphism (see Andrew Myers' answer)
* immutable data structures (many languages)
* lazy evaluation (Haskell)
but that would make a good language. A language is not functional if you use call by ref.
If you look at the new list Clojure. Some of them are implemented but other that CL has are not and that makes for a good language.
Clojure for example added some:
ad-hoc polymorphism lazy evaluation immutable data structures Type inference (most dynamic languages have compilers that do that)
My Answer is:
Scheme schooled stay as it is. CL could add some ideos to the standard if they would make a new one.
Its LISP most can be added with libs.