Hacker News new | ask | show | jobs
by cderwin 3235 days ago
If "expanding your brain" is really what we want to optimize, why not learn Haskell? Or for that matter, why not learn something even more strongly typed, like Agda or Idris? Or even a theorem prover like Lean?

Most of the reasons the author presents would either be expanded in one of those languages, or is immaterial to the goal of maximizing learning. After reading the article I have no more reason to consider learning Clojure over learning any of the languages above.

7 comments

One huge difference is the workflow you have in Clojure. REPL driven development is an experience unique to Lisps. When you're working with Clojure, any code can be run in the context of the live app straight from the editor as you write it. This is an amazing experience and very mind expanding in what a development process can look like.

Also, anybody who wants to try FP style programming, but isn't interested in static typing is much better off with Clojure. Type systems in languages like Haskell and Idris add a lot of complexity and mental overhead that's not present in a dynamic language.

I don't think that's entirely unique to Lisps, you can get a REPL into an existing Erlang instance very easily. More easily than getting nREPL running in Clojure, actually. I believe Smalltalk as well, although I'm less confident on that one as I haven't used it much.
As someone who's professionally developed mostly in Erlang and Clojure in the last 6 years, it's not even close. I love Erlang, but the REPL is severely underpowered and used mostly for debugging and inspection.
The difference is in the editor integration. When you work with Lisp, your editor is connected to the running instance of the application. You have shortcuts to reload any symbol in the application at runtime directly from the editor.
I work in Scala and it has live code update, interactive worksheets and a repl.

As for type systems increasing complexity I think that depends on the application. As a beginner to Clojure I would pass unstructured data around all over the place and then have the mental overhead of trying to remember the structure or fix it all at runtime.

The biggest difference is the editor integration. Scala, like most languages has a REPL that's basically a toy you use on the side. Clojure REPL is tightly integrated into your workflow.

I did a talk last year where I illustrate it starting around 15 minute mark https://www.youtube.com/watch?v=nItR5rwP4mY

I'm not aware of any non-Lisp languages that provide anything close to that.

The REPL driven workflow directly addresses the problem you're describing as well. When I'm working with Clojure, I never write a lot of code before running it. Each time I write a function, I run it and see exactly what it's doing. This means that I never have to keep a lot of context in my head.

Let's say I need to pull some data from the database, massage it, and return it to the client. I would write the function to read the data, run it to see what shape of the data I get. Then, I would write the function that consumes that data and transforms it. Again, I'd run it and see that I have the data I want, and so on. At each step of the process I only need to consider the last step and the next.

Also, Spec and Schema are commonly used in libraries to provide the description of the data at the API level. This is where I really care what the shape of the data is, as opposed to typing every single function I write.

Nice talk. I agree with you that the repl in Clojure and Common Lisp etc is much more of a first class citizen in the development environment. What people often don't realize is how powerful the repl and worksheet can be in scala development.
Thanks, having editor integration with the application runtime completely changed my workflow. I simply couldn't go back now, having instant feedback on the code you write is extremely satisfying.

What surprises me is that none of mainstream languages provide this kind of environment. Lisp and Smalltalk have been around for many decades, and somehow this workflow ended up being completely neglected in the mainstream.

This is what clojure.spec is meant to address. It's helped me, but I am actually trying to get some momentum in Haskell now... I have a saying, "Constraints free the mind." Frameworks, Type systems, etc., provide the constraints we need to let our mind think about the actual problem instead of suffering from analysis paralysis.
Is there something about clj's repl that makes it any different from ghci's?
As an Emacs user, from my experience the Clojure REPL has much better integration with my editor than any non-LISP I've used, which includes Haskell and Erlang. Most of the time I'm not typing commands into the REPL - instead, I'm evaluating code inline, which means I don't need to jump back and forth from the source file to the REPL. It's a much more interactive and productive experience for me, and I haven't been able to fully replicate anywhere else - though it's possible I haven't discovered the right tools for those other languages yet.

I use CIDER with Emacs for Clojure integration: https://github.com/clojure-emacs/cider

When the experience is amplified by homoiconic data structures and structural editing, yes -- exploratory, ephemeral metaprogramming is a breeze. Compound Clojure data literals have a fully readable print representation that can be generically manipulated by built-in core library functions. Say I have a DSL that takes a bunch of nested maps and vectors as an input spec and compiles them to nested closures, and I want to create a large spec derived from some other source satisfying certain constraints, but it's not worth defining yet a higher-level DSL for a one-off definition that I intend to commit to source control. I can generate that data structure any way I like and spit the result out at the REPL or in an interactive buffer, then effortlessly plop that structure or any of its substructures into an EDN file or just inside a `def` or `let` binding in a regular Clojure file, which I then pass to the DSL compiler. It's much simpler than manipulating things like GADTs in Haskell. Even in Common Lisp, it might require some ad-hoc parsing code to accomplish the same thing.
Yes, you can reload any symbol in the CLJ REPL directly from the editor. You can also evaluate any function within the context of the running application without going through the main. The REPL is an integral part of the development process in Clojure. You can see this in action here around 15 minute mark https://www.youtube.com/watch?v=nItR5rwP4mY
R and RStudio are actually quite good in this aspect.

- You can evaluate any part of editor in console with keyboard shortcut. The only difference between using console and editor is that your input is easily saved in editor.

- The environment browser make inspecting variables, data structures much easier.

Besides, in R you want to use vectorized functions for better performance, so you search for general functions and combine them, which actually promote good functional programming style instead of a big control block with many processes intertwined.

The type system is only one part of a programming language. You can, quite reasonably, have two languages with exactly the same type system that wind up with vastly different ways of programming them due to what you're given as primitives/the standard library.

Programs aren't usually directly portable between languages primarily for that reason - the language designers optimise for a specific way they want their language to be used, then everyone else follows, and eventually you're going against an entire ecosystem if you want to do something different.

Absolutely agree. However there is a "perfect language" for every programmer to learn, depending on their background and spare bandwidth. For many programmers the jump from their primary language to Haskell or others is too high, not only in terms of complexity but also utility. Several colleagues of mine are picking up new languages right now and a common criteria is they want to build something tangible/useful within a limited time. Having access to the JVM and third party libraries they already know is certainly a plus.
All the languages you've mentioned will also expand your brain, but in a different direction, mostly related to correctness and provability of specifications.

Clojure will expand it in different ways. Here's some examples:

  - Interactive programming
  - Persistent data structures
  - Immutability
  - Code that writes code
  - Functional programming 
  - Extendable polymorphism
  - Lazy evaluation
  - Eager evaluation
  - Parallel computing
  - Dynamic variable extent
  - Recursion
  - Concurrent programming
  - Declarative programming
  - Aspect oriented programming
  - Logic programming
  - Code as data
  - Software transactional memory
  - Generative testing
  - Contracts guards
  - Optional type systems
  - Conditional restarts
  - Monads
  - Variant types
  - Collection abstractions
  - Array programming
  - Map Reduce
  - List comprehensions
  - Reactive programming
  - And more...
They're not lisps, and do not have proper macros.
What's "improper" about Template Haskell, other than the easily-avoided phase restriction?
I think people should learn both Haskell and Clojure :). However, while Haskell is very obvious in its differences with more commonly used languages, I feel that Clojure is more subtle.

I felt like an idiot while I was learning Haskell; but in Clojure I only realised my idiocy after perhaps a year of working in it. Clojure feels deceptively close to more standard languages, but in its own way is as different to them as Haskell. It just isn't as immediately obvious.

Or Typed Racket if one wants typing and lisp. I don't know how core.typed compares to typed Racket, but that might also be enough to get the flavor of types?