The future is long. Computers will be around for thousands of years. They've only been useful for the last ~60 years.
A programming language needs at least two things to succeed: a niche, and corporate backing. Almost every successful language has had both.
Lisp's benefit is that it is discoverable. Programmers hundreds of years from now will continue to stumble across the roots of lisp: http://www.paulgraham.com/rootsoflisp.html
http://ep.yimg.com/ty/cdn/paulgraham/jmc.lisp
The axioms are too simple not to discover by accident.
What are some promising niches for Lisp?
I think gamedev is a likely candidate. The whole gamedev industry still uses C++ / C#. But Lisp can be just as fast -- you can even make a statically-typed Lisp, which avoids any possibility that Lisp's performance will bite you.
The benefits for gamedev in particular are immense: http://all-things-andy-gavin.com/2011/03/12/making-crash-ban...
The reason this hasn't happened is because it takes a certain personality type to write languages. In 99% of cases, your language will fail. Meaning, if you spend 3 years on it, that's 3 years you didn't spend learning React or Vue or %salary-du-jour. You have to love it.
That love is quite the feeling. When you design your own language from the ground up, and break down all barriers of complexity, it stops mattering whether anyone else ever uses your language. Making it was reward enough.
In concrete terms, I think self-hosted Lisps are a promising way forward. It's simple to bootstrap a Lisp in almost any language: JS, Lua, Python, Ruby. Write a reader (you can use JSON to start), then write a compiler that steps through the tree of expressions and spits out the native language constructs. E.g.
(%while true
(print "she sells C shells by the C store"))
becomes C#:
while (true) {
Console.WriteLine("she sells C shells by the C store");
}
or Python:
while True:
print("she sells C shells by the C store")
etc.
Then you can save the output code to disk and run it.
At this point you've written your compiler in Python, Ruby, or whatever. Now the trick:
Create a file "compiler.l" that sits alongside your "compiler.py" file. For each function in compiler.py, translate it into your Lisp language and save it into compiler.l.
E.g. if your compiler.py file has:
def compile(x):
if atom(x): return compile_atom(x)
if special(x): return compile_special(x)
return compile_call(x)
then in your compiler.l file, you should write:
(define compile (x)
(if (atom x) (return (compile_atom x)))
(if (special x) (return (compile_special x)))
(return (compile_call x)))
And hey presto -- your compiler.py file is now capable of reading in compiler.l and generating
the exact same code. Meaning you no longer have to program in Python! (Or whatever language you're targeting.) At that point your language is fully self-hosted, and you can extend it however you like.
It's so incredibly easy to set up a self-hosted Lisp that it almost seems like a toy. But it's incredibly powerful. E.g. I'm currently building one on top of Racket so that I can write traditional unhygenic macros, completely sidestepping Racket's syntax transformer system.
You could imagine doing something similar for React, letting you write programs to generate React components -- a step toward the ultimate templating system.
Once you've done this, you'll notice that the entire language is extremely small. It's a thin wrapper around the host language. But that's the value -- that's why it's useful. E.g. in many Lisps, it's common to use the symbol 't for True and an empty list for False. In those systems, there is no such thing as a "boolean" type. Everything is either an empty list or not-an-empty-list. You could do that, which demonstrates just how powerful this technique is. Just modify the way you compile IF statements:
(define compile_if (cond a b)
(+ "if (" (compile cond) ") { "
(compile a)
" } else { "
(compile b)
" }\n"))
to
(define compile_if (cond a b)
(+ "if (yes(" (compile cond) ")) { "
(compile a)
" } else { "
(compile b)
" }\n"))
(i.e. wrap COND in a call to a YES function.)
Then you can define YES as:
(define yes (x)
(return (not (or (== x false) (empty_list x)))))
Now your compiler spits out "if (yes(x)) { a } else { b }" everywhere, and your YES function is your definition of truthiness.
So, you can do that, and it works, but at that point you'll discover that you start wrestling with the underlying language. If you change how IF behaves, then you'll need to change how AND and OR behave, too. Stuff like that.
I've found the best strategy is to make your Lisp as close to the target language's semantics as possible. When you do that, you end up with a tiny runtime. Clojure's runtime is gargantuan because it has to define in Java all of Clojure's semantics. But here, you're using the host language's semantics directly.
Now it might seem that this "isn't really a Lisp". But it turns out that it's just as powerful as any other Lisp. All you have to do is change your compiler from:
(define compile-file (src)
(compile (read-file src)))
to
(define compile-file (src)
(compile (expand (read-file src))))
Then define an EXPAND function that performs macroexpansion on the expressions you got from the reader.
At that point it's straightforward to introduce:
(define-macro when (cond . body)
(return `(if ,cond (do ,@body))))
which compiles to:
def when_macro (cond, *body):
return ["if", cond, ["do"] + body]
environment["when"]["macro"] = when_macro
Your EXPAND function becomes dead simple:
def expand(form):
if atom(form): return form
mac = environment[form[0]]["macro"]
if mac: return apply(mac, form)
return map(expand, form)
Finish it off by defining DEFINE-MACRO:
def define_macro_macro(name, args, *body):
f = eval(compile(["fn", args] + body))
environment[name]["macro"] = f
environment["define-macro"]["macro"] = define_macro_macro
Presto, now you can write
(define-macro add1 (x) (return (list "+" x 1)))
(print (add1 41))
and it compiles to print(41+1).
Scott Bell and Daniel Gackle pioneered the above technique, and I've spent the last couple of years implementing it across different languages, including Python, elisp, ruby, and racket.
You can try it out here:
https://github.com/sctb/lumen
That's Lumen, a Lisp for JS and Lua. One of the coolest aspects is that it compiles to both JS and Lua simultaneously, and runs in both.
If you want one for Python, you can try out my branch:
https://github.com/shawwn/lumen/tree/features/python
It compiles to JS, Lua, and Python simultaneously. So the same codebase runs on node, lua, luajit, torch, python2.7, python3, and pypy.
LuaJIT has a fantastic FFI library -- and since our lisp runs in Lua, that means we automatically get a fantastic FFI: https://github.com/sctb/motor/blob/master/pq.l
Imagine how much work it would be to write your own FFI! I think it's one of the best FFI's of any Lisp implementation.
So that's a rough outline of Lisp's unique power. It's so versatile that it feels like just a matter of time until someone decides to embed it in some popular system, like a game engine or an online store builder. Clojure might get some serious competition within the next decade.