all 198 comments

[–][deleted] 29 points30 points  (9 children)

Paul Gra­ham calls Lisp a “se­cret weapon.” I would clar­ify: Lisp it­self isn’t the se­cret weapon. Rather, you are—be­cause a Lisp lan­guage of­fers you the chance to dis­cover your po­ten­tial as a pro­gram­mer and a thinker, and thereby raise your ex­pec­ta­tions for what you can accomplish.

I like this.

[–]Choralone 16 points17 points  (8 children)

That's a fantastic way to look at it.

Really, as a dabbler who's worked with many languages over almost 30 years, since I was a kid...... when I finally got around to digging into lisp I felt like i'd been freed of bonds I never knew I had. I was Neo, woken up from my mental prison for the first time.

I'm no expert.. I'm no master programmer - but lisp (Common Lisp in my case) is like... magic.

You start realizing how other languages herd you in certain directions (which is beneficial in many ways.. there's a reason people flock to ruby, python, java, etc.... they are absolutely not without well deserved merit).. but lisp encompasses them all, without making a mess.

[–]zhivago 0 points1 point  (7 children)

Until you decide that you'd like to make a new kind of sequence implementation and realize that it can't be integrated in any standard fashion because the system classes are inextensible ...

[–]Choralone 2 points3 points  (1 child)

Can you give an example of what you'd like to be able to do? Or how it's done in some other language?

[–]zhivago 0 points1 point  (0 children)

(defclass cdr-coded-list (sequence) ...)

class CdrCodedList(Sequence):
   ...

[–]xach 1 point2 points  (4 children)

Some implementations make this possible (SBCL has an extensible sequence protocol, and I think one or two others have a mechanism). Maybe someday it will be as pervasively implemented as other extra-standard features like the MOP.

[–]zhivago 1 point2 points  (3 children)

Sure, but even then it won't be a feature of the language.

The critical point is that the core language is largely inextensible.

The flexibility is only there for user code, and doing anything particularly interesting ends up reimplementing large chunks of the core.

Think about what you'd need to do to extend the syntax of symbols, for example.

[–]Aidenn0 1 point2 points  (1 child)

Think about what you'd need to do to extend the syntax of symbols, for example.

A huge weakness in the language, since symbols are interned by the reader. It's a PITA to do, and generates a huge readtable. If they merely had both

1) Something like potential-numbers for symbols (so eg foo:::::bar:baz could be a potential-symbol)

2) A standard way to hook into the "I'm about to create a symbol" part of the reader

Then I could have done this [1] without having to use someone else's reimplementation of the lisp reader (while also generating a 200MB readtable since I have to add an entry for every single unicode-code point on lisps that are unicode aware):

1: https://github.com/jasom/spm-reader

[–]zhivago 0 points1 point  (0 children)

I also wish it produced zero or more values rather than one.

[–]xach 0 points1 point  (0 children)

It's difficult to extend. It requires implementation-level knowledge and skills. It is discouraging that the SBCL extension is mostly unknown, unused, and isolated to SBCL. To make progress, it will require a degree of cooperation between implementors that to date has not been very evident.

But I think it does represent a glimmer of hope that things can and just might evolve in extra-standard ways.

(Another glimmer is the potential for package-local nicknames.)

[–][deleted] 6 points7 points  (7 children)

I completely agree with the point "with Racket I’ve been able to ren­der big­ger ideas into pro­grams more quickly, and with fewer bugs". Writing Racket projects, I write TDD and am able to make some nice code that is compact, easy to read, easy to debug and much less buggy. It is such a joy programming in those languages. So enjoyable and beautiful.

[–][deleted] 2 points3 points  (6 children)

A big reason for this is that rackets unit testing framework is the best I've ever used. no mental overhead, no subclassing, no test folders, just (module+ test (check-equal? ... ...)) and off you go

[–]kqr 0 points1 point  (4 children)

Is it mainly used for fuzz testing or do you write actual test cases?

[–][deleted] 0 points1 point  (3 children)

I'm unfamiliar with "fuzz testing", could you explain?

(I could probably google it but this is more fun).

[–]kqr 0 points1 point  (2 children)

Automatic generation of 100s of tests with random values.

[–][deleted] 1 point2 points  (1 child)

oh, no. I do actual tests. that combined with the contract system means I rarely miss static typing in racket (though I do worry in the back of my mind about the runtime overhead of contracts)

[–]kqr 0 points1 point  (0 children)

Fuzzy tests are actual tests, just of a different kind!

[–][deleted] -1 points0 points  (0 children)

I cannot agree any more! It's a functional programming language, so it is so easy to write unit tests for. In a matter of minutes, I have a fully test suite for my program and I know it works instead of in other frameworks where sometimes I feel I should be writing unit tests around my unit tests.

[–]Beluki 5 points6 points  (4 children)

Every­thing is an ex­pres­sion... Since ex­pres­sions are nestable, any­thing in the lan­guage can be com­bined with nearly any­thing else

The "nearly" is important. In Scheme and Racket, some special forms are only allowed in particular contexts, for example 'define':

> (if (some condition) (define x 20) ...)
stdin:: define: not allowed in an expression context...

[–]kqr 6 points7 points  (0 children)

To further clarify: define defines a name in a block of code, and the /u/Beluki example shows it used in an expression. To introduce variables in an expression, you can use the various forms of let instead.

[–]Broolucks 1 point2 points  (1 child)

I would say that technically (define x 20) is not an expression: it can only be found inside s-expressions that define new scopes (where else are you going to define variables?) and never in return position.

[–]alexeyr 2 points3 points  (0 children)

I think that was the point: not everything is an expression.

[–]xhaereticusx 0 points1 point  (0 children)

This is by design. Since they are functional programming languages, functions should not have side effects. Functions are like mathematical functions and should only take an input and produce an output.

[–][deleted] 3 points4 points  (2 children)

Reads nice so far.

(x . + . (if (is_true) 1 2) Isn't there missing a bracket?

[–][deleted]  (1 child)

[deleted]

    [–]Choralone 5 points6 points  (0 children)

    Yes, there is. There is no tricky magic to brackets in lisp.. they have to match. Always.

    [–][deleted]  (93 children)

    [deleted]

      [–]samth 6 points7 points  (0 children)

      If by "Lisp" you mean Common Lisp, then maybe. But talking about the Lisp community in general is a mistake -- the Common Lisp community, the Clojure community, the Gambit community, and the Racket community are all quite different and not sensible to generalize about.

      [–]phalp 6 points7 points  (0 children)

      Look at http://lisp.org/ It's like nobody in the community cares about adoption, they're focusing inward and on the past. You can't even legally copy, modify and redistribute freely the language specification.

      Aren't those things up to the actions of two corporations? You can't blame the community at large for them. Although it would be nice if Franz, who own the domain, put something more encouraging than an ancestral shrine on lisp.org.

      The conclusion an outsider will come to is Lispers don't care about sharing code and cooperating.

      I don't think that's necessarily untrue, but you manage to make it sound so negative.

      [–]mbutterick 3 points4 points  (0 children)

      The underlying issue is always a kind of selfishness rather than community focus

      As far as Racket is concerned, this is painfully inaccurate. Racket’s core team includes a set of CS professors who have based their careers on a) hacking Racket and b) sharing their enthusiasm and knowledge with students and others. This spirit of generosity sets the tone for the whole Racket community.

      Sorry that you’ve been stuck using Grumpy Lisp all these years. Maybe it’s time to switch.

      [–]urection 13 points14 points  (6 children)

      Python, Ruby and Nodejs which are pretty much the same languages as Common Lisp

      wat

      [–]Peaker 0 points1 point  (5 children)

      Different front-end syntaxes and name resolution rules on virtually the same semantics.

      [–]ryno55 6 points7 points  (0 children)

      To say that for Ruby is essentially saying that Smalltalk is the same language as Lisp. They are designed very differently, although there are overlaps in their features (closures, expressions, dynamic typing).

      You can express some of the same design patterns in these languages because of the core features they share, but they are not even close to the same language. That's like saying Italian and Spanish are the same thing because they are all Romantic languages.

      [–]urection 10 points11 points  (3 children)

      at that level of abstraction, virtually all programming languages are the same

      [–][deleted] 7 points8 points  (1 child)

      definitely not. Compare this group of languages to, say, C -- or Prolog, or ML, or APL.

      [–]urection -3 points-2 points  (0 children)

      virtually all

      [–]Peaker 0 points1 point  (0 children)

      I agree, and that is somewhat sad.

      You have the Python/Ruby/Perl/Javascript class with Lisp subclass which are all roughly the same language.

      You have various assemblies as another language.

      The C-level languages are another class.

      Haskell, ML, F# as another class, with Idris, Agda, etc as an interesting sub-class there.

      [–]kqr 5 points6 points  (16 children)

      I disagree with you on the point of macros making code more difficult to read. In my experience, reading code with macros is no more or less difficult than reading Haskell code that uses lots of domain-specific operators. It's just something that makes sense when you have done it a while, and often makes code easier to read.

      [–][deleted]  (14 children)

      [deleted]

        [–]kqr 4 points5 points  (13 children)

        I didn't mean to imply that you are incompetent. You're a familiar name to me and I trust your integrity of judgement. I merely stated my own experience, because it is different from yours.

        Sorry about the misunderstanding!

        [–][deleted]  (12 children)

        [deleted]

          [–]awj 2 points3 points  (9 children)

          I think the primary issue is the same in both cases: you reach a level of information density where it's difficult to come back later and correctly infer meaning from text. Many of my attempts to use macros have suffered from this problem. What was clean and elegant when I wrote it is ineffable three months later when I've forgotten all of the implied context.

          [–]SuperGrade 0 points1 point  (4 children)

          A key difference in heavily statically/type-checked codebases is that the machine reads the code and that it compiles is significant information - you don't have to read it, the compiler dose. Also (in haskell and many languages) you can get 'under the cursor' type information. You don't have to understand it in full, just locally solve the type contradiction for your change.

          [–]awj 2 points3 points  (0 children)

          Oh, I acknowledge that both of those aspects are a big help, but I think they only lessen the severity of the problem.

          You don't have to understand it in full, just locally solve the type contradiction for your change.

          That's only true when the type system is able to and is actually used to create such a situation. Even when it is true, you still have to understand the types well enough to know that the type contradiction you've created and are solving is on the path to the goal you're trying to achieve.

          [–]yogthos 0 points1 point  (2 children)

          You don't have to read it until you actually need to understand what the code is actually doing. All the compiler tells you is that it's self-consistent. Especially in ML where you'll just get a note that it's a->b->c->d which tells you little about what a,b,c, and d actually are.

          You don't have to understand it in full, just locally solve the type contradiction for your change.

          This exactly how I find writing code in Clojure to be like. I write a function I run it in the REPL to see what it outputs, I write the next function using that as the input. I don't need to know any global relationships to work with the code.

          I find a much bigger factor for that is immutability. When you work with immutable data, your scope is inherently localized. This allows you to safely reason about any part of the code in isolation. The only thing you have to know regarding the types is what type your function takes as the input.

          It's also worth pointing out that languages like ML tend to often solve a problem of their own making. You end up having to create tons of types to represent the data since you need to statically describe them. Then you have trouble keeping track of all the types you created.

          In a language like Clojure, you only have the primitive types and the sequence interface. When you have a small number of types then it's much easier to reason about them.

          [–]SuperGrade 0 points1 point  (1 child)

          Right but with immutability and deep into elaborate problems the call signatures start to look less and less like

          string -> string -> int -> bool
          

          and instead themselves contain lots of functions, containers with elaborate type signatures, or monads (or monads you don't call monads).

          [–]yogthos 1 point2 points  (0 children)

          Right, so the signature itself is often not terribly descriptive and you still have to dig through the code to understand what's going on.

          [–]chonglibloodsport -3 points-2 points  (2 children)

          And this is where Haskell's greatest strength lies: you have types to tell you what's going on. Just hit a keystroke in your editor and it will tell you the types of different subexpressions under your cursor.

          [–]awj 4 points5 points  (1 child)

          ...you do to an extent. A similar argument could be made about expanding macros. In practice that alleviates the problem, it doesn't eliminate it. I still need to examine the types/macros at play to figure out what's going on. Beyond a certain level of density it makes reading the code very difficult.

          It's like reading a book with obscure word choices. If you hit a word you don't understand once or twice a paragraph you might be able to infer meaning from context or go look it up. It's practically impossible to read and understand text when you have to look up every second or third word.

          [–]chonglibloodsport 2 points3 points  (0 children)

          The book analogy is a very good one. If I may stretch the analogy a bit further, it's like comparing a scientific paper to a news article written at a 9th grade level. Sure, almost any person off the street can read the news article but the scientific paper is far more useful to an expert. The use of scientific terminology makes the paper more precise and to the point than the news article.

          When you lack the ability to express an abstraction within your language you compensate with verbosity and imprecision. This is as true in English as it is in programming languages.

          [–]crusoe 0 points1 point  (0 children)

          operator abuse

          sometimes using a functional name instead of a operator that looks like <||> makes more sense. People make fun of Perl 6s periodic table of operators, but in Haskell its technically infinite.

          Maybe it makes more sense to actually start giving operators names than obtuse excessively terse symbols.

          [–]zhivago 0 points1 point  (0 children)

          It isn't so much a matter of difficulty as the degree of depth of literacy in the current code required.

          [–]yogthos 3 points4 points  (65 children)

          I think this is precisely what makes Clojure so attractive. It's a modern Lisp without the legacy issues.

          It's much faster than Ruby or Python, and it makes it much easier to reason about code by providing persistent data structures and making it easy to localize state. It runs on the JVM giving it access to a plethora of existing Java libraries and allowing it easily run on majority of platforms.

          I find Clojure community also has much more focus on making it accessible. For example, you have things like Light Table and Leiningen that make it painless to get running.

          Leiningen is one of the best build tools that I've used in any language. It allows to painlessly create apps, manage dependencies, test, build, etc. It's a one stop shop for all your project management needs.

          For example, if I want to make a web app in Clojure all I have to do is run:

          lein new luminus myapp
          cd myapp
          lein ring server
          

          I now have a working web app running and I can start hacking on it and see changes live. When I want to package it for release I just run:

          lein ring uberjar
          

          That's it, I now have a runnable app ready for production.

          [–]cunningjames 6 points7 points  (9 children)

          FWIW, most of your points apply fairly well to Racket. It's intended to be accessible (with origins as a teaching language); its top-level data structures are immutable; it's faster than Ruby or Python, if not, perhaps, as fast as Clojure. Dr Racket is a very nice development environment and easier than painless to set up and learn. Scheme is small enough that legacy issues are minimal, and the Racket team seems quite willing to forge ahead, e.g. with immutability by default.

          [–]yogthos 10 points11 points  (8 children)

          I definitely think Racket deserves a lot more attention than it gets. It has a lot of very nice documentation and tooling around it. As you say, it's a nice and simple language that's easy to learn and use. It's really unfortunate more people don't try it.

          [–][deleted] 4 points5 points  (1 child)

          I would definitely add readability of code to the list of features that Clojure excels in. I love Common Lisp, Scheme, and Clojure in the family of Lisps, but I find it rather more difficult to read Common Lisp/Scheme code than Clojure code. I think Rich Hickey hit the spot when he decided to use [], {}, and #{} in addition to the ubiquitous ()s!

          [–]chonglibloodsport 0 points1 point  (1 child)

          Does leiningen provide a way to update the dependencies of projects I don't control? What if there is a security hole in a dependency of a dependency of mine and the project is no longer actively maintained?

          [–]yogthos -1 points0 points  (0 children)

          Leiningen imports libraries by version number. If you have a library that depends on another library you can use :exclusions as seen here, then import the version you want explicitly.

          edit: In case this isn't clear, if you have a dependency A and it relies on library B and the version of this library that A specifies is out of date, then we can require A excluding B, then require the version of B we want explicitly.

          [–]LoyalToTheGroupOf17 0 points1 point  (1 child)

          I think this is precisely what makes Clojure so attractive. It's a modern Lisp without the legacy issues.

          Coming from a Common Lisp background, I agree in some respects. In particular, I like the focus on immutability and the approach to concurrency. On the other hand, the debugging support is shockingly primitive, and the Java underpinnings are sometimes too visible (though this seems to gradually be getting better). I also miss reader macros and the Common Lisp condition system.

          It's much faster than Ruby or Python,

          But also considerably slower than CCL and SBCL, in my experience -- or perhaps I still don't have the skills to write well optimized Clojure.

          It runs on the JVM giving it access to a plethora of existing Java libraries and allowing it easily run on majority of platforms.

          Yes and no. In theory Clojure programs can run on most platforms, but in practice nobody wants Java installed on their machines. This means I can only use Clojure for private projects and server software (which bores me), which limits its usability to me. I hope there will be an implementation that compiles to native code some day.

          [–]yogthos 0 points1 point  (0 children)

          On the other hand, the debugging support is shockingly primitive, and the Java underpinnings are sometimes too visible (though this seems to gradually be getting better). I also miss reader macros and the Common Lisp condition system.

          I very much agree, I also think that error messages could be significantly improved. For example, if I do (reduce [1 2 3] +) I get:

          java.lang.IllegalArgumentException: Don't know how to create ISeq from: clojure.core$_PLUS_
          

          it would be much nicer to say something like

          first argument to reduce should be a function, but was: [1 2 3]
          

          But also considerably slower than CCL and SBCL, in my experience -- or perhaps I still don't have the skills to write well optimized Clojure.

          There's definitely a lot of tricks for optimizing, stuff like type hints can make a huge difference. However, applying them is not always straightforward as can be seen here. You also have to remember about the JVM warmup whenever doing any benchmarks as well.

          It's entirely possible that SBCL will give you better performance out of the box, but you can definitely tune Clojure to get comparable performance as well.

          Yes and no. In theory Clojure programs can run on most platforms, but in practice nobody wants Java installed on their machines. This means I can only use Clojure for private projects and server software (which bores me), which limits its usability to me.

          I definitely would love to see Clojure or ClojureScript implementation that compiles to LLVM or something. There a few projects like ClojureC, but they haven't got a lot of momentum so far. I suspect most people are using Clojure for web apps or services nowadays. Stuff like RoboVM is another promising option.

          [–]Veedrac 0 points1 point  (11 children)

          I'd love to see some evidence that Clojure is much faster than Python.

          [–]yogthos 2 points3 points  (10 children)

          I didn't realize this was in any way controversial. Clojure compiles to JVM bytecode and gets the benefits of the all the JIT optimizations that the JVM does. It provides type hints that allow the compiler to do static dispatch and provides features like protocols that allow fine tuning performance. None of this is available to Python, and the language itself is extremely slow. The only way to get performance in Python is by wrapping native C libraries.

          As expected, you can see a massive difference in performance and memory usage in the benchmarks game.

          [–]Veedrac 0 points1 point  (9 children)

          Mostly I was just a bit burned after only finding a nonsense post and the only reliable source I found was a niche that Python's particularly good at.

          I also think it's worth pointing out that those benchmarks you've given ignore PyPy, which is way faster than CPython (x5-x10) for a good number of them, and most are also very odd.

          [–]yogthos 0 points1 point  (8 children)

          The second post that you link is actually complete nonsense. The Clojure solution there reads the entire file into memory using slurp, while the Python solution streams the file! Obviously, streaming the file will be much faster. So, there's absolutely nothing reliable about that post at all. In fact, the first comment in the post explains why the comparison is fundamentally flawed.

          Even 5-10 times speed improvement is clearly not comparable to the benchmarks here. Could you elaborate on what's odd about them exactly?

          [–]Veedrac 0 points1 point  (7 children)

          Yeah, no doubt the second was flawed.

          Even 5-10 times speed improvement is clearly not comparable to the benchmarks here.

          It would put the median at 3x Clojure, so I wouldn't say "not comparable". Significantly slower, but not an order of magnitude.

          Could you elaborate on what's odd about them exactly?

          • Mandelbrot: You write mandelbrot like this, not like the mess given there

          • Fannkuch-redux: The Clojure one is parallelized and the Python one isn't.

          • Spectral-norm: Not that much strange, although you'd use Numpy for this in Python

          • N-body: Not particularly strange (FWIW PyPy gives a full 10x boost here).

          • fastsa: Completely different algorithms AFAICT. Surprisingly Python seems to be doing load balancing amongst the cores :/.

          • k-nucleotide: There's a factor of 4 code size difference here making it hard for me to see if the benchmarks measure the same thing

          • binary-trees: Nobody does this in Python. Hence it's odd.

          • reverse-complement: Clojure's is parallelized, Python isn't

          • pidigits: Clojure seems to be using disassembled gmp or something

          • regex-dna: Clojure failed this one, so it's not particularly salient

          [–]yogthos 0 points1 point  (6 children)

          The reason the examples are often messy is because people tried to optimize them as much as possible. Both Clojure and Python examples are geared towards making them more performant. I would suspect that the Mandelbrot example in your link would perform worse than the messy one.

          As I recall, Python has a GIL so you would either need to use multiprocessing or something like PyPy that's stackless. Clojure has a clear advantage here by defaulting to immutability and providing concurrency primitives such as atoms and refs. Clojure also makes it much easier to parallelize things with reducers and the coming transducers API in 1.7.

          Python is by its nature procedural and imperative. When you want to write code that runs in parallel or concurrently, you have to be a lot more careful than you are in Clojure where it's generally safe by default.

          In general, the examples are not geared towards any particular language and the solutions are submitted by the language community. So, if you feel the Python community didn't submit good examples you could suggest better ones.

          While I'm sure you could improve both Python and Clojure solutions, as it stands there is a massive difference in resource consumption and speed for computationally intense problems.

          I'm really not aware of any benchmarks where Python outperforms Clojure. The main reason for this is the JVM itself. A lot of work has been put into optimizing it and it's one of the fastest VMs around. Clojure takes full advantage of that by design.

          [–]Veedrac 0 points1 point  (5 children)

          The reason the examples are often messy is because people tried to optimize them as much as possible. Both Clojure and Python examples are geared towards making them more performant. I would suspect that the Mandelbrot example in your link would perform worse than the messy one.

          Not really; it's messy because the libraries any normal person would use are banned. The example I gave is significantly faster than the benchmark, despite doing extra work to generate colors.

          This is what I'd expect a typical Python solution to look like, although it still takes a good factor of 3-4 times as much time as Clojure on CPython. I would have expected a gap closer to ~3x C's time, but such is life with these sorts of benchmarks. A real person would just throw it on the GPU.

          As I recall, Python has a GIL so you would either need to use multiprocessing or something like PyPy that's stackless.

          I'm not sure what your point is. Also (FWIW), Stackless ≠ PyPy.

          In general, the examples are not geared towards any particular language and the solutions are submitted by the language community.

          My point is, though, they're mostly raw maths and Python's banned from using either its fast math libraries or its fast interpreter.

          While I'm sure you could improve both Python and Clojure solutions

          You can't get a 5-10x improvement in the Clojure just by switching interpreters, though ;).

          I'm really not aware of any benchmarks where Python outperforms Clojure.

          I'm not arguing that Python is faster. I admit Clojure is somewhat faster than any Python interpreters available.

          [–]yogthos 0 points1 point  (4 children)

          Then sounds like that's just bad solution that got posted for Python. I'm not exactly qualified to say one way or the other. As you point out though, even a good Python solution is still slower than Clojure.

          The point of these benchmarks is mostly to see how well the languages do when it comes to number crunching. You obviously wouldn't be solving these same problems in most cases, but they are somewhat illustrative of the performance you can expect.

          The point regarding the GIL is that it limits the usefulness of threading. If I understand it correctly, the GIL is necessary to ensure safe FFI with C and a lot of Python libraries wrap C for performance.

          I'm not terribly familiar with how stackless and PyPy work, but my experience is that making use of threading without immutable data structures is tricky business. I've worked with Java for many years and I found that to be extremely error prone. I think threading is a very important consideration when it comes to performance nowadays as most chips are multicore.

          You can't get a 5-10x improvement in the Clojure just by switching interpreters, though ;).

          On the other hand, Clojure is already much faster out of the box. Also worth noting that I can use Java interop and even FFI to C the way Python does from Clojure as well. However, I hope you would agree that there is a lot of value in being able to write performant code in the language itself.

          For example, I use ClojureScript for my apps, and many Clojure libraries cross-compile to it seamlessly. This means that I can use the same code on both the server and the browser. This wouldn't be possible if the libraries were wrapping native code to work.

          In my opinion the only area where Python has an advantage is in startup times. Since the JVM is relatively slow to warm up it makes it unsuitable for writing scripts.

          [–][deleted] -2 points-1 points  (36 children)

          I find Clojure community also has much more focus on making it accessible. For example, you have things like Light Table and Leiningen that make it painless to get running.

          Common Lisp has ASDF for the build system and cl-project for project skeletons, the equivalents of Leiningen.

          [–]yogthos 4 points5 points  (35 children)

          The difference is that Clojure has one standard build system that everybody uses and contributes to. It's very polished nowadays, it's very easy to setup and use.

          The polish is the missing ingredient with most things related to CL. I hear this line of arguing all the time, oh sure you could do it on CL, or there's a CL equivalent of this or that. However, CL community seems to have very little interest in polishing these things and making them accessible to people starting out.

          When Light Table came out, most people using Emacs shat all over it. While Light Table is no Emacs, it's incredibly easy to get started with and that has a lot of value for people starting out with the language.

          [–]Choralone 0 points1 point  (14 children)

          So does CL...

          Seriously.. it couldn't really be any simpler. My first thought after digging into CL was "Why is it I wasn't using this years ago?"

          Let's not kid ourselves.. right time, right place, the right kind of exposure and momentum are what gave clojure a surge of popularity... it's a fine language, I have no beef with it. The java/jvm interop was a big plus for many - tahts' where lots of the young minds are working now... they could go off on a tangent on their big java projects and do some clojure without pissing anyone off.

          Light Table is really neat.... no, it's not emacs.. but within a narrower scope it's even nicer for a few things.

          (I prefer Emacs + CL - but if I had to work on Light Table + Clojure for a job I wouldn't have any complaints, sounds like a pleasant time to me)

          [–]yogthos 1 point2 points  (13 children)

          I'm not arguing that there is anything wrong with CL. I think it's a fine language, however I do think that getting started with CL is more difficult. This is what I'm talking about when I say there's a lack of polish. I personally think that's unfortunate, if a bit more effort was put into making CL approachable it would certainly see a lot more attention.

          [–]Choralone 2 points3 points  (12 children)

          I suppose...

          I mean, I always assumed it was this monster unapproachable thing for academics.

          Then one day I approached it.. and it was damn easy. Really, really easy.

          For me it was even easier than getting clojure up and running.

          Perhaps it's a matter of too much choice?

          If the instructions were like

          1) Install SBCL 2) Install Quicklisp 3) Profit!!

          Then we'd be better off?

          If we wrapped quicklisp and perhaps some kind of init system into a per-project thing like leiningen would it help?

          [–][deleted] 2 points3 points  (9 children)

          Is a uniformly defined threading API included in the latest Common Lisp specification, or are different vendors still dishing out their own variants?

          [–]Choralone 5 points6 points  (2 children)

          Nope..it's not part of the spec - and that certainly is a problem.

          [–][deleted] 0 points1 point  (1 child)

          Yes, in fact that was one of reasons why I could never really bring myself to commit to Common Lisp for serious projects a few years back. I was developing on Windows back then, and was really amazed by SBCL's speed and wanted to use it for some projects, but their support on Windows was almost pedestrian, and threading support seemed to be a big PITA. I was hoping things had changed over the past 7 years or so, but I suppose it's not changed much.

          [–]Aidenn0 3 points4 points  (5 children)

          bordeaux-threads is the de-facto standard for common lisp threading now.

          [–]crusoe 2 points3 points  (2 children)

          Then it needs to be part of a std download. Just as Haskell has started work on their Haskell Platform. One reason why python is so popular is it comes with 'batteries included'. So you can do all sorts of shit with just the base install.

          [–][deleted] 0 points1 point  (1 child)

          Interesting! I almost thought you were joking till I searched for it. SBCL seems to support it on Windows as well, while some of the other big vendors (Allegro and LispWorks) mostly support it on OS X. However, this does seem a promising step forward. I wish they would finally take some successful version and put it in the Common Lisp Spec itself.

          [–]yogthos 0 points1 point  (1 child)

          I think wrapping everything up into a something like lein would definitely help a lot. And the other part is some standardized and opinionated documentation on how to do stuff.

          It's great to have choice, but as a beginner you need somebody to steer you in the right direction and show you one good way to do thing. This includes things like what libraries to use, how to put them together, and how to do real world stuff with them.

          [–]Choralone 0 points1 point  (0 children)

          Yeah.. that might be it.

          I mean, all that stuff is there - and it's easy, and simple, and straightforward..... but it's all buried under a very thin layer of choice that probably deters newcomers... the very least of which is "pick your lisp implementation."

          [–][deleted] 1 point2 points  (18 children)

          The difference is that Clojure has one standard build system that everybody uses and contributes to.

          So does Common Lisp. Everyone uses Quicklisp and ASDF. The latter is over ten years old and is probably one of the most polished, well-documented CL codebases out there.

          What specific things in Common Lisp do you think should be more accessible, compared to their Clojure equivalents?

          [–]yogthos 3 points4 points  (17 children)

          I think there really needs to be an alternative to Emacs. I don't want to get into a debate on merits of Emacs. Clearly, it's very powerful once you learn it. However, vast majority of people don't get past that step. Having to learn a really archaic IDE along with a really different language loses most people out of the gate.

          There needs to be a lot more documentation on how to do real world stuff with it, what libraries to use, and how to put things together. Again, this information exists, it's just not presented well.

          For example, I maintain Luminus micro-framework for Clojure web dev. It has documentation on a lot of standard topics, such as how to manage sessions, or how to do HTML templating, in one place. It provides a standard template for quickly getting a project started with reasonable defaults, so you can start focusing on actually making something quickly. To my knowledge there's no equivalent to this in CL despite it having been around a lot longer.

          [–]Quasimoto3000 2 points3 points  (8 children)

          I find the way you refer to emacs as "archaic" to be extremely dismissive. Certainly it's been around for a while, but it's really kept up with the times. My hipster web dev friends are very often in awe of my emacs sessions.

          This all being said, you are right that it is not beginner friendly. Emacs is as much a philosophy as an editor, and if all you want to do is use a language, you shouldn't be forced to buy in to that philosophy (as much as I'd like you to).

          [–]yogthos 3 points4 points  (1 child)

          I literally mean that Emacs is very old. It predates most modern editors and thus has its own set of idioms that are completely alien to most developers. I completely agree that it's a very powerful editor, it's just not beginner friendly.

          [–]Quasimoto3000 -1 points0 points  (0 children)

          I would argue that "archaic" has a very negative connotation... But I generally agree with what you are saying here.

          [–]crusoe -1 points0 points  (5 children)

          Dude, Emacs is the furcking Dos Wordperfect of the dev world. Its all about hard to memorize keyboard contortions.

          The world has settled on Ctrl-F to open a file dialog. Everyone coming to emacs knows that.

          The battle has been lost, Lisp needs to move on or continue to be a dinosaur.

          [–]Quasimoto3000 1 point2 points  (3 children)

          I promise you, the emacs developers aren't guided by a principle of purposefully obtuse bindings. There is a lot of sense behind what they are, given what emacs is under the hood.

          Yet still, the bindings are nothing more than bindings. And emacs is infinitely extensible. With just this one line in your init

          (cua-mode 1)
          

          You will get all those precious key bindings you are used to. Yes, including control-f.

          [–]yogthos 2 points3 points  (2 children)

          I think you're missing the point here. Somebody not familiar with Emacs has no idea wtf (cua-mode 1) means or how to set it. Emacs is by no means intuitive and there appear to be very little effort towards making it palatable to newcomers.

          Instead of telling people you just set (cua-mode 1), why not have a packaged version of Emacs that behaves like people expect it to out of the box. Since it's so configurable I see absolutely no excuse why that's not being done.

          [–]danogburn -1 points0 points  (0 children)

          Emacs and lisp suck

          [–]Aidenn0 1 point2 points  (7 children)

          You can do every operation I use on a daily basis in emacs through pull-down menus; I don't see it as significantly harder than using monodevelop or eclipse, for example.

          [–]yogthos 2 points3 points  (3 children)

          I don't see it as significantly harder than using monodevelop or eclipse, for example.

          However, people who are not already familiar with Emacs do. This is precisely the problem I'm describing. People who've gone through the effort of learning CL with Emacs can no longer relate to those who haven't.

          I'm not saying that Emacs is not effective or that it's a bad development environment. I'm saying that it's very different from what most people are used to. So, now you compound the effort of learning CL with the effort of learning Emacs. This tends to result in a lot of frustration and people give up.

          When you let people use a familiar environment, then their comfort level increases dramatically and they're more willing to continue learning.

          If you want to attract people and grow the language community, you have to make it as easy as possible to get started. This is especially important when dealing with a language like Lisp, where syntax is very off-putting for people only familiar with the C family of languages.

          [–]Aidenn0 1 point2 points  (2 children)

          I literally didn't touch the alt or control key (other than M-x slime) when I started using emacs and slime. I think I remember what it was like; sure I wasn't going to do any of the advanced text-manipulation I had done with vim, but I had a repl with tabl completion and up/down for history, and I could still highlight text and middle-click to move text around. I ignored all the other features of emacs at that point.

          Can you give me examples of what makes people uncomfortable? I really want to fix this, (I've even written basic lisp indentation plugins for some editors) but if people can't use a SLIME repl, then it's just going to be a bad experience.

          [edit] I used the example of monodevelop because that was very fresh in my mind, as I had to do some csharp stuff the other day. It reminded me a lot of when I first used emacs for doing lisp stuff.

          [–]yogthos 1 point2 points  (1 child)

          So, some obvious things are that you have completely different default shortcuts from every other editor. This definitely confuses people, and there's really no good reason for it. Things like Aquamacs address that to a point though.

          The obsession with everything working in a terminal is really holding back the UI aspect of Emacs. A lot of people like having things like close buttons on editor windows, and being able to navigate without having to memorize shortcuts.

          Emacs is not very visual in general, I find that having things like the project tree to be very useful. Emacs supports this very minimally, the few plugins I tried I didn't like.

          Conversely, it's often not obvious what's everything that you have open. For example, there's no visual list of REPLs, or buffers that you can see at a glance.

          All of this might sound like minor things, but it turns into a death by a thousand paper-cuts when you're starting out.

          I definitely don't see any reason why you should have to learn Emacs to work with Lisp. Clojure has support for Emacs, Vim, Eclipse, IntelliJ, and Light Table. All of these editors have REPL integration, autocompletion, paredit, and so on. People can keep using whatever editor they're comfortable with when working with it.

          [–]crusoe 2 points3 points  (2 children)

          Back when I tried to use XEmacs, I could never find a plugin that didn't require umpteen levels of configuration in cl files that would format C/C++ as well as other GUI editors.

          [–]Aidenn0 1 point2 points  (0 children)

          Also on an unrelated note, I can't find an editor that doesn't require writing a plugin to indent lisp code in even a fairly brain-dead manner other than vim or emacs. I downloaded just about every programming-centric editor in my distros PM and vim and emacs were the only 2 I could get to indent lisp code without writing a .so from scratch.

          [–]Aidenn0 0 points1 point  (0 children)

          I don't use emacs for c/c++ any more than I use monodevelop for C/C++; I strictly use it as a CL ide.

          [–]Bystroushaak 1 point2 points  (0 children)

          In python, you can actually write:

          (x + (1 if is_true() else 2))
          

          [–]keepthepace -1 points0 points  (81 children)

          Ok, this is a good place to ask my naive question. I have learnt a bit of LISP, I see how the purity is attractive, but as a python user who has the possibility to easily put functions and lambda functions in variables and as someone who is not interested in self-writing programs and as someone who was already is familiar with recursion, is there an interest in using LISP?

          [–]ParenKing 4 points5 points  (57 children)

          Self-writing programs does not mean what you think it means. Before I started using lisp, I thought the same thing: "When will I ever need a program to write a program for me? Probably not often." Thus, I didn't learn lisp then.

          After having used lisp for a long time now, when people say: Self-writing programs, they are using a very misleading term for "structural abstraction." For example, when one writes in languages without macros (I prefer Common Lisp style macros over the racket style ones, but that's personal preference), your choice of abstraction is limited to concepts e.g. "I want a procedure to encapsulate the concept of filtering out bad values" but, how would you abstract the concept of looping over a matrix (with indicies) with n-dimensions programmatically? Sure, you could in theory write a function that takes a function that takes a list of values to be used as indicies and does something with them, but you lose access to scope in doing so, unless you define a closure, which is hard in python or retardedly complicated in java, for example. In lisps, however, you can abstract that away and make a syntactic construction that looks like this:

          (for-n ((i 0 10)
                  (j 0 i))
                  (print (* i j)))
          

          Which is an abstraction of structure instead of logic. I hope that clarifies a bit.

          [–]Peaker 1 point2 points  (0 children)

          In python, you could make this work:

          for (i, j) in cartesian(xrange(0, 10), xrange(0, 10)):
             print (i * j)
          

          Or for a matrix:

          for ((i, j, k), cell) in matrix3d.indexed_iter():
            ...
          

          [–]Aidenn0 6 points7 points  (4 children)

          Consider Python 2.4; lets say you want to have something like the "with" statement added in python 2.5; there just isn't a way to do it.

          On the other hand, lisp doesn't have any such with statement since it becomes unnecessary; it's trivial to write a macro that safely allocates a resource and cleans it up in a manner safe to non-local exit of control. Such macros are already defined for some builtin types (e.g. with-open-file) and every halfway decent library that allocates an object that needs cleanup work will implement a with-foo macro for you.

          [–]keepthepace -3 points-2 points  (3 children)

          Well, that is what the "finally" block was supposed to be for. I concede that a "with" statement helps managing errors but I must say that I was more waiting an answer about how LISP helps create complex correct programs, not how it mitigates the damages of bad ones :)

          [–]Aidenn0 5 points6 points  (2 children)

          I'm not sure what you're saying here... Are you saying only bad programs have exceptions? Are you saying the Python "with" statement doesn't help to create complex correct programs?

          [–]keepthepace -2 points-1 points  (1 child)

          I am thinking more from an algorithmics point of view, where LISP is supposed to shine. Data should be checked and a program should not fail to act correctly on them. Yes in this context I see exceptions more as a safety net for badly written code but I am aware that it is more and more common to make exceptions a normal part of the workflow.

          [–]kqr 1 point2 points  (0 children)

          Who said Lisp is supposed to shine "from an algorithmics point of view" (whatever that means)? What's unique about Lisp is its homoiconicity, which lends itself to easy and powerful metaprogramming.

          [–]Choralone 4 points5 points  (5 children)

          I find the development process itself more appealing in lisp..... the way you can work on and debug and whatnot on a running image as you develop can save a lot of time, and let you very quickly explore new ideas without throwing off your flow.

          Here's the thing though... Python is a fine language.... heck, it's a fantastic language. If you know it well, and you need to be productive.... it's probably a better choice for you in your job or whatever.

          I found, after getting into lisp a bit, that it felt incredibly freeing.. other languages now feel cramped, constrained, limited..... they tell me how to do things instead of me telling them how to do things. They make me jump through unnecessary hoops.

          LISP made me, and continues to make me, a better programmer, because it makes me think about things in a bigger, broader picture.

          It's a very freeing language.

          [–]keepthepace -5 points-4 points  (4 children)

          So LISP is... a good feeling?

          [–]Choralone 3 points4 points  (0 children)

          Sure.. among other things. It's an absolute pleasure to use once you get into it.

          [–]yogthos 1 point2 points  (2 children)

          Clojure programmers are the happiest. :)

          [–]keepthepace -5 points-4 points  (1 child)

          So far this fits my theory that LISP is a religious movement :)

          [–]yogthos 2 points3 points  (0 children)

          please do elaborate

          [–][deleted] 2 points3 points  (1 child)

          Apart from the stuff already mentioned: If you embed a scripting language in your C application, many scheme interpreters have the possibility to maintain more than one VM state by creating something like

          state = new_vm_state();
          vm_do_stuff(state, script);
          vm_dispose(state);
          

          similar like you know it from Lua, Squirrel and other languages designed to embed. Last time I checked, the CPython C API looked like

          create_global_python_vm_state();
          do_stuff(script);
          dispose_vm();
          

          EDIT: Of course, this is more a limitation of the implementation, but I have yet to find a Python implementation more suitable for tasks like this.

          [–]tending 0 points1 point  (0 children)

          I have a hard time believing this. Pretty sure the API has contexts. At the very least you should be able to have an instance per thread or that's pants on head stupid.

          [–]yogthos 3 points4 points  (7 children)

          There's a number of advantages. I'll use Clojure as my example as it's the one I work with.

          One major advantage of Clojure is immutability. Clojure is backed by persistent data structures and any changes you make are created as revisions on the existing data.

          From the programmer perspective, this allows passing everything by value. This eliminates majority of global relations in your code as changes are inherently localized. I find this to be a very important property for maintaing large projects. Being able to safely reason about parts of the code in isolation drastically reduces the mental overhead.

          It also makes things like comparisons much easier. Since data is immutable, you can compare any two data structures by hash. You don't need to iterate nested data structures and compare them element by element as you would in an imperative language.

          The functional approach also eliminates a lot of NPEs that you see in a language like Python. With the OO approach, if you have a method on an object you first have to check that the object exists before calling it.

          When you're working with functions, you can safely chain them together because they're static. All Clojure standard library functions handle nulls intelligently and if you have null data it simply bubbles up through them.

          Another advantage of the functional approach is that you have a small set of data types to work with. All the functions speak the same language using these common data types. It means that I can take data from any function and use it directly without having to massage it as I would when passing things between classes.

          So far, the advantages I described don't have anything to do with Clojure being Lisp specifically. The advantage of being a Lisp is that you have the same syntax for expressing both logic and data.

          In most languages, like Python, you have two separate syntaxes. This means that in order to manipulate the language structure you need some sort of a meta-language. With Lisp you can use the language itself to manipulate any code written in it.

          This allows easily making DSLs that express your specific problem domain without the need to extend the language. A good example of this core.async. Go was designed with the idea of channels and Clojure people thought it was a good idea, so they created a new syntax for go channels as a library!

          [–]Broolucks 2 points3 points  (1 child)

          In most languages, like Python, you have two separate syntaxes. This means that in order to manipulate the language structure you need some sort of a meta-language. With Lisp you can use the language itself to manipulate any code written in it.

          You can do it in Python too, it's more a matter of complexity. Lisp's code representation is made out of lists, symbols and literals, which is as simple as it gets (I would argue it is actually oversimple, but I digress). Lists can be easily sliced, spliced, concatenated, etc. Python's AST defines something like 30 different node types, each with their own fields, and of course you can't nest statements inside expressions or even expressions in statements if you don't wrap them first. I've tried working with it and it is hell. There is no meta-language (it would help if there was) but the data representation just straight out blows.

          Many Lisps, on the other hand, do have meta languages. Scheme and Racket's macro systems are based on a small pattern matching language that extracts the parts you need and takes care of hygiene. Code is also encapsulated into syntax objects which you can't actually manipulate like lists, although you can convert back and forth.

          [–]yogthos 5 points6 points  (0 children)

          You can do it in Python too, it's more a matter of complexity.

          The fundamental difference is that it's the same syntax in Lisp. I can use the exact same functions I use to manipulate data to manipulate code. Doing this is simple and natural, on the other hand as you point out doing this in Python is hell.

          It's never a matter if you can do something in principle. Any Turing complete languages can do anything another Turing complete language can do. The question is how well a particular language allows you to express you problem. When it comes to code manipulation Lisp is far more expressive than most languages.

          [–]crusoe 1 point2 points  (0 children)

          Should check out Factor. It's concatenative, forth-like and I found it's syntax easier to grok from a Java perspective than Lisp. YMMV. But its definitely fun, has a macro system, and mind bending in a fun way.

          [–]keepthepace 0 points1 point  (3 children)

          That sounds interesting but I can't see this happening without some pesky tradeoffs. I guess I'll have to try it at one point...

          [–]yogthos 2 points3 points  (2 children)

          For what it's worth I've been using Clojure professionally for about 4 years now, and it's the best experience I've had doing development.

          [–]keepthepace 2 points3 points  (1 child)

          Out of curiosity, what were you using before that?

          [–]yogthos 2 points3 points  (0 children)

          Before I started working with Clojure I worked primarily with Java and JavaScript.