The author of the blogpost is pretending to be "one of us" (people that get lisp) - in "Lisp devotees (myself once included)" - but apparently they never understood it if they're still thinking that lisps' advantages "make Lisp into an unweidly, conceptual sledgehammer that’s often out of scale with the problem being solved.". The word "often" there also makes me think they're making a point without experience or proper evidence.
They're a lisper who broke their teeth in Clojure. I think when people like me disdain Clojure's lispness is because we think it doesn't really teach you the philosophy behind it. This is not an argument, just my perception.
They're also in favor of code censorship (let's remember that censoring is detrimental to creative processes):
"Giving any programmer on your team the ability to arbitrarily extend the compiler can lead to a bevy of strange syntax and hard-to-debug idiosyncrasies. "
I use Racket in production along with my team and may I suggest a humble, easy solution: one person makes a pull request, another reviews the pull, and if there are new macros introduced we can discuss it with the team to see if it's necessary. It's so simple. The blogpost author is making a big deal out of nothing. To prefer a language that doesn't allow that power because the author has a problem trusting others instead of choosing to communicate with their team members is appalling.
The author also keeps mentioning Python's "simplicity". How can anything be as simple as (function args)? I'm yet to understand what people that argue this point mean by "simplicity".
Then the author talks about static checks. "How can a static analysis tool keep up with a language that’s being arbitrarily extended at runtime?". Simple, do macro expansion before static type checking, as Typed Racket does.
They're also still playing with SQL DSLs too. I think that's such a waste of effort. SQL is already a DSL for talking to the DB. I don't want another layer because I'm not going to be manipulating SQL in my code, because "SQL" has nothing to do with the problem domain I'm working on. At that point any SQL queries have already been abstracted away inside functions that have meaningful names like associate-product-to-customer or whatever. I don't want to talk about SQL ever in my problem domain abstraction layer. Using SQL DSLs as an argument against macros with the angle of static type checking is a poor argument because SQL DSLs are usually for people that use mutable code anyway. I use Typed Racket's DB library and its querying functions work together with the type system to let me know if I'm not handling some potential kind of value that might come from the database.
The author then mentions Unix's consistency. Unix couldn't even decide on a standard notation for command line arguments. Then onto that fallacy that reality is object-oriented. Objects can't possibly be as composable as functions because Objects break down into methods (which are not composable, are not first class, etc) whereas functions (lambdas) can make up everything and can really be thought of as an atom for computation (i.e. Lambda Calculus).
Complaints like "Clojure has nine different ways to define a symbol" are moot. Pick one that your team likes and go with it. On to the next thing. Also, to argue against Lisps by arguing against Clojure is like arguing against democracy by arguing against the Democratic Republic of the Congo.
I do believe the blogpost author is severely misguided in their criticism. To say things like "Python wants the conceptual machinery for accomplishing a certain thing within the language to be obvious and singular" while ignoring the fact that lisps machinery is obviously much simpler and obvious and singular - again, (function args) - is disingenuous. It does make me believe that all their SICP reading was for nothing (I've only lightly skimmed SICP and I don't pretend to have read it).
The author does acknowledge (en passant) that certain Schemes (they don't identify which) don't suffer from this complexity (which makes the whole post look more like a criticism of Clojure). I'd invite the author to look into Racket.
They say that lisps "impose significant costs in terms of programmer comprehension". My experience is that if you divide your layers of abstraction correctly you will be able to work in the problem domain layer where nothing is obscure. And that layer is built from smaller in the layer below, parts that are also clear in what they accomplish because they only do one thing in their abstraction level. I've found that following this rule of only doing one thing per function makes for code that is easy to understand all the way from the bottom to the top layers. Programming this way, however, is the classic, boring way to write code [1], and because it's not a fad I guess people aren't too much into it.
Also to the point above, having already rewritten a significant portion of a Rails legacy app into Racket with the help of my coworkers, it seems that lisps introduce more understanding and shed more light onto the code, precisely because it makes everything explicit (we code in functional style so we pass every argument a function needs) and does away with "Magic" that Rails and rails fans like so much. When something gets annoying to write we implement our own "magic" on top of it, not in terms of silly runtime transformations that lesser languages like Ruby need to resort to, but through dynamic variables (Racket calls them parameters), monadic contexts, etc, i.e. things that can be checked at compile time.
And finally: "I think it’d be irresponsible to choose Lisp for a large-scale project given the risks it introduces". Well, the only risk I've personally witnessed is the very real risk of your coworkers starting do dislike more mainstream, faddish languages like Python and Ruby, because they don't allow the same freedom and simplicity and explicitness that lisp does (lisp has a long tradition of making things first-class, which consequently makes these things explicit).
[1] We use top-down design to decide what the interface for a given abstraction layer will look like, and bottom-up to decide which functions should be written in the layer below; then we cycle that process by refining the layer below through the same process of defining its interface top-down and then the layer below it as bottom-up. And we use algebraic type checking along with contracts to enforce post-conditions and properly assign the blame to the right portion of the code to speed up debugging. These are all old techniques.
By "simplicity" they mostly mean "what I'm used to". Same applies for people calling "Windows simpler than Linux". It's the same argument over and over: illegibility (because you're not used to it), too much power to handle (because you're not used to it)...
Complaints like "Clojure has nine different ways to define a symbol" are moot. Pick one that your team likes and go with it. On to the next thing. Also, to argue against Lisps by arguing against Clojure is like arguing against democracy by arguing against the Democratic Republic of the Congo.
It's a valid complaint. I think the quote by John Carmack:
"Everything that compiler will allow will at some point be written"
Describes the situation well.
In other words, since Lisp pretty much allows all, anything will be written in Lisp. When you look at Lisp function, it might do what you want, or it might not. There are no guarantees it does what it claims to do. So, chances are you either have supreme belief the guy behind it did his end of the deal, or you are writing your own. And with all the power Lisp offers why not write your own? And this is generally what I think is the failing point. It brings NIH of the highest magnitude to software development.
We're a team of 6 programmers, 3 of which code Typed Racket. We all started here as Rails devs and got tired of it. I'm always pushing to invest time in research for better solutions and our team seems to be open to that (after weeks of discussion, though - we're no heaven). Typed Racket is the solution we found after surveying the field. Our management has recently told us they believe we're now a good-sized team given the projects we have to maintain. If it gets out of hand I'll be glad to know that there are people we can hire to work with these technologies. We're based in NYC.
Since you don't have any contact info in your profile, I'd love to talk to you about your use of Typed Racket, and whether there's anything you can tell us about how to improve it. Feel free to email me at samth@cs.indiana.edu
Always happy to hear of people using my software. :)
How are you liking Racket instead of Rails? I'm assuming you're using it for web development.
I was a Rails dev that moved to Clojure and I don't know much about Racket at all, but how is the library situation? Of course in Clojure if I ever need something I can count on being able to find it in Java and interop with it.
To be fair and not to mischaracterize the situation, I have been programming in lisp for at least 5 years in personal projects, so it's not like I had to learn it now and I also did not convert from Ruby to lisp.
I never liked Rails. I don't like anything that focuses on files, because in my mind the fact that code needs to be saved in the filesystem is simply incidental, so no code should rely on that fact; but Rails builds on top of that, telling you where to put your code (folder structure), taking control from you over what code sees what code (MVC), giving you command-line tools to use when it could simply provide functions at a REPL (gems, migrations, tests), etc. It simply doesn't get it at all.
In my opinion, a framework (loosely speaking) that solves only the easy problems (how to organize code) is pointless. But something like an FRP library that allows me to do GUI by focusing on how I want to transform data, and liberating me from thinking about events and callbacks - THAT to me is something that solves a hard problem.
So to answer your first question, I'm happy not to need to touch Rails again - right now I only need to look at it, rewrite it in Racket, and delete it.
Racket is a much nicer system than Clojure, it has immensely intelligent people behind it, the documentation is stellar (unbelievably so, in my opinion), there are libraries for everything you can imagine and more (like an FRP library). It also does native GUI everywhere, effortlessly. For things it lacks, it is easy to write an FFI.
I would never touch Java nor trust any library written in Java. The more I code the more I distrust code that relies on mutability. I haven't reached 100% pure code yet but I work towards that goal, not away from it.
EDIT: Yes, I am using Racket for a webapp that connects to a database and perform general CRUD operations.
Clojure is Java. I fundamentally disagree with everything Java. Java is also clunky. The environment that needs to be installed and maintained for Clojure is a lot more complex and subdivided than the one for Racket (which is an all-in-one batteries-included deal). I believe Clojure might be a good option for those coming from Java, if they want to breathe a little, but if I'm already free, it would be a step backwards for me. Racket is a true Scheme (regardless of whether they like being called that).
Also, I already knew Scheme and its simplicity is very appealing to me. If there's a feature lacking, I can implement it. Not true for Clojure, which lacks several important foundational features (TCO, continuations...).
Why would you choose Clojure over Racket? (I'd only be interested in hearing the reasons someone that isn't a Java programmer would have).
Comments
The author of the blogpost is pretending to be "one of us" (people that get lisp) - in "Lisp devotees (myself once included)" - but apparently they never understood it if they're still thinking that lisps' advantages "make Lisp into an unweidly, conceptual sledgehammer that’s often out of scale with the problem being solved.". The word "often" there also makes me think they're making a point without experience or proper evidence.
They're a lisper who broke their teeth in Clojure. I think when people like me disdain Clojure's lispness is because we think it doesn't really teach you the philosophy behind it. This is not an argument, just my perception.
They're also in favor of code censorship (let's remember that censoring is detrimental to creative processes):
"Giving any programmer on your team the ability to arbitrarily extend the compiler can lead to a bevy of strange syntax and hard-to-debug idiosyncrasies. "
I use Racket in production along with my team and may I suggest a humble, easy solution: one person makes a pull request, another reviews the pull, and if there are new macros introduced we can discuss it with the team to see if it's necessary. It's so simple. The blogpost author is making a big deal out of nothing. To prefer a language that doesn't allow that power because the author has a problem trusting others instead of choosing to communicate with their team members is appalling.
The author also keeps mentioning Python's "simplicity". How can anything be as simple as (function args)? I'm yet to understand what people that argue this point mean by "simplicity".
Then the author talks about static checks. "How can a static analysis tool keep up with a language that’s being arbitrarily extended at runtime?". Simple, do macro expansion before static type checking, as Typed Racket does.
They're also still playing with SQL DSLs too. I think that's such a waste of effort. SQL is already a DSL for talking to the DB. I don't want another layer because I'm not going to be manipulating SQL in my code, because "SQL" has nothing to do with the problem domain I'm working on. At that point any SQL queries have already been abstracted away inside functions that have meaningful names like associate-product-to-customer or whatever. I don't want to talk about SQL ever in my problem domain abstraction layer. Using SQL DSLs as an argument against macros with the angle of static type checking is a poor argument because SQL DSLs are usually for people that use mutable code anyway. I use Typed Racket's DB library and its querying functions work together with the type system to let me know if I'm not handling some potential kind of value that might come from the database.
The author then mentions Unix's consistency. Unix couldn't even decide on a standard notation for command line arguments. Then onto that fallacy that reality is object-oriented. Objects can't possibly be as composable as functions because Objects break down into methods (which are not composable, are not first class, etc) whereas functions (lambdas) can make up everything and can really be thought of as an atom for computation (i.e. Lambda Calculus).
Complaints like "Clojure has nine different ways to define a symbol" are moot. Pick one that your team likes and go with it. On to the next thing. Also, to argue against Lisps by arguing against Clojure is like arguing against democracy by arguing against the Democratic Republic of the Congo.
I do believe the blogpost author is severely misguided in their criticism. To say things like "Python wants the conceptual machinery for accomplishing a certain thing within the language to be obvious and singular" while ignoring the fact that lisps machinery is obviously much simpler and obvious and singular - again, (function args) - is disingenuous. It does make me believe that all their SICP reading was for nothing (I've only lightly skimmed SICP and I don't pretend to have read it).
The author does acknowledge (en passant) that certain Schemes (they don't identify which) don't suffer from this complexity (which makes the whole post look more like a criticism of Clojure). I'd invite the author to look into Racket.
They say that lisps "impose significant costs in terms of programmer comprehension". My experience is that if you divide your layers of abstraction correctly you will be able to work in the problem domain layer where nothing is obscure. And that layer is built from smaller in the layer below, parts that are also clear in what they accomplish because they only do one thing in their abstraction level. I've found that following this rule of only doing one thing per function makes for code that is easy to understand all the way from the bottom to the top layers. Programming this way, however, is the classic, boring way to write code [1], and because it's not a fad I guess people aren't too much into it.
Also to the point above, having already rewritten a significant portion of a Rails legacy app into Racket with the help of my coworkers, it seems that lisps introduce more understanding and shed more light onto the code, precisely because it makes everything explicit (we code in functional style so we pass every argument a function needs) and does away with "Magic" that Rails and rails fans like so much. When something gets annoying to write we implement our own "magic" on top of it, not in terms of silly runtime transformations that lesser languages like Ruby need to resort to, but through dynamic variables (Racket calls them parameters), monadic contexts, etc, i.e. things that can be checked at compile time.
And finally: "I think it’d be irresponsible to choose Lisp for a large-scale project given the risks it introduces". Well, the only risk I've personally witnessed is the very real risk of your coworkers starting do dislike more mainstream, faddish languages like Python and Ruby, because they don't allow the same freedom and simplicity and explicitness that lisp does (lisp has a long tradition of making things first-class, which consequently makes these things explicit).
[1] We use top-down design to decide what the interface for a given abstraction layer will look like, and bottom-up to decide which functions should be written in the layer below; then we cycle that process by refining the layer below through the same process of defining its interface top-down and then the layer below it as bottom-up. And we use algebraic type checking along with contracts to enforce post-conditions and properly assign the blame to the right portion of the code to speed up debugging. These are all old techniques.
By "simplicity" they mostly mean "what I'm used to". Same applies for people calling "Windows simpler than Linux". It's the same argument over and over: illegibility (because you're not used to it), too much power to handle (because you're not used to it)...
Right on. Jeff Raskin's equation is pure gold -
It's a valid complaint. I think the quote by John Carmack: "Everything that compiler will allow will at some point be written" Describes the situation well.
In other words, since Lisp pretty much allows all, anything will be written in Lisp. When you look at Lisp function, it might do what you want, or it might not. There are no guarantees it does what it claims to do. So, chances are you either have supreme belief the guy behind it did his end of the deal, or you are writing your own. And with all the power Lisp offers why not write your own? And this is generally what I think is the failing point. It brings NIH of the highest magnitude to software development.
Tests, contracts, type checking - those are the guarantees. It's not unlike any other language.
Is your team hiring?
We're a team of 6 programmers, 3 of which code Typed Racket. We all started here as Rails devs and got tired of it. I'm always pushing to invest time in research for better solutions and our team seems to be open to that (after weeks of discussion, though - we're no heaven). Typed Racket is the solution we found after surveying the field. Our management has recently told us they believe we're now a good-sized team given the projects we have to maintain. If it gets out of hand I'll be glad to know that there are people we can hire to work with these technologies. We're based in NYC.
Since you don't have any contact info in your profile, I'd love to talk to you about your use of Typed Racket, and whether there's anything you can tell us about how to improve it. Feel free to email me at samth@cs.indiana.edu
Always happy to hear of people using my software. :)
We've talked a bunch on IRC - thanks for making my life easier. :)
How are you liking Racket instead of Rails? I'm assuming you're using it for web development.
I was a Rails dev that moved to Clojure and I don't know much about Racket at all, but how is the library situation? Of course in Clojure if I ever need something I can count on being able to find it in Java and interop with it.
To be fair and not to mischaracterize the situation, I have been programming in lisp for at least 5 years in personal projects, so it's not like I had to learn it now and I also did not convert from Ruby to lisp.
I never liked Rails. I don't like anything that focuses on files, because in my mind the fact that code needs to be saved in the filesystem is simply incidental, so no code should rely on that fact; but Rails builds on top of that, telling you where to put your code (folder structure), taking control from you over what code sees what code (MVC), giving you command-line tools to use when it could simply provide functions at a REPL (gems, migrations, tests), etc. It simply doesn't get it at all.
In my opinion, a framework (loosely speaking) that solves only the easy problems (how to organize code) is pointless. But something like an FRP library that allows me to do GUI by focusing on how I want to transform data, and liberating me from thinking about events and callbacks - THAT to me is something that solves a hard problem.
So to answer your first question, I'm happy not to need to touch Rails again - right now I only need to look at it, rewrite it in Racket, and delete it.
Racket is a much nicer system than Clojure, it has immensely intelligent people behind it, the documentation is stellar (unbelievably so, in my opinion), there are libraries for everything you can imagine and more (like an FRP library). It also does native GUI everywhere, effortlessly. For things it lacks, it is easy to write an FFI.
I would never touch Java nor trust any library written in Java. The more I code the more I distrust code that relies on mutability. I haven't reached 100% pure code yet but I work towards that goal, not away from it.
EDIT: Yes, I am using Racket for a webapp that connects to a database and perform general CRUD operations.
I would like to hear more about your reasons for choosing Racket over Clojure.
Clojure is Java. I fundamentally disagree with everything Java. Java is also clunky. The environment that needs to be installed and maintained for Clojure is a lot more complex and subdivided than the one for Racket (which is an all-in-one batteries-included deal). I believe Clojure might be a good option for those coming from Java, if they want to breathe a little, but if I'm already free, it would be a step backwards for me. Racket is a true Scheme (regardless of whether they like being called that).
Also, I already knew Scheme and its simplicity is very appealing to me. If there's a feature lacking, I can implement it. Not true for Clojure, which lacks several important foundational features (TCO, continuations...).
Why would you choose Clojure over Racket? (I'd only be interested in hearing the reasons someone that isn't a Java programmer would have).
I was under the impression that Clojure has been designed around immutability, and Racket not so much... ?
But it sits on top of mutable Java libraries.