Hacker News new | past | comments | ask | show | jobs | submit login
How much can a Clojure developer do alone? (yyhh.org)
113 points by jgrodziski on March 30, 2021 | hide | past | favorite | 102 comments



> There is no other language that places such an emphasis on programming directly with plain and naked data literals.

Counterexamples: Erlang and Elixir. Arguably also Prolog. Also Lua. Many Schemes, and Racket. Of course, REBOL and Red. TCL probably, too.

I can't help but think that people making claims of "no other language has X" are in most cases wrong, and should study a bit more before making them.

There's a lot of hype in the article, I don't want to diminish the value of REPL-based workflows or conciseness[1] of the language, but the overall message that any fresh graduate can become a 10x programmer in under a month... Well, there's at least nothing humble about it, despite the article touting "humility" as a virtue of prospective Clojurists.

[1] Why aren't we all writing in APL/K/J if it matters that much?


I haven’t written any Clojure for a few years now. So my knowledge and technique is out of date, but I have a contrary opinion on “repl driven” development vs many Clojure advocates. In short: your repl compiled code is state that you now have to track in your head.

Yeah, great, you can compile a single function and it hot loads. It’s pretty awesome for experimenting and debug, but the longer your repl session is open and the more you change and compile, the more likely you are going to end up with a mixed state in memory that does not reflect how your program would behave if run from scratch.

You are going to forget to save a change or your manual tinkering with a data structure will leave it inconsistent. These errors accumulate, like navigation error with an IMU.

It’s got use cases for sure, and I miss hot reloading when I’m debugging in other languages, but it is far from being the ultimate feature a language can have.


I was very productive in Clojure for some number of years and never once, not a single time, found benefit in REPL-driven development. I've used it here and there just to test little snippets, but never actually used it for real.

Frequently I found it unnecessary, anyway. Clojure just made it way to easy to get things right that I rarely had to dive in and figure out why something was wrong.

These days I prefer types over Lisps for that, but people change.


Totally agree. I found Clojure to be an amazing, well-designed language. I found the REPL to be secondary to really great immutable data structures and pragmatic escape hatches.


I think this gets to the issue that there are two different ideas of "REPL driven".

In one, you're using the REPL frequently in production. In the other you're using it during development. Those are two different scenarios and result in different perspectives.

Just like many (most?) organizations have gone to (varying degrees of) infrastructure as code versus tweaking each server manually, REPL-in-production is similarly problematic. As you noted, you end up in a position where you aren't actually sure of the system state (both what it is and how it got there). This is a use of the REPL that should be minimized (if you're using it to actually change the system state versus trying to understand the system state, especially).

However, when working on a new system you may tweak a single or small cluster of servers manually and build out your automation scripts based on that experience. Similarly, the REPL during development is a way to quickly prototype, experiment, and test code but needs to end up committed to actual source files for deployment.


If you write your code the correct way, there wouldn't be any states in your namespaces for you to keep track in your head. That only happens if your code is full of `(def some-state (atom {}))` or something like that.

You are basically illustrating my point about "You probably cannot learn Clojure all by yourself". You need a mentor to show you how to do Clojure correctly, or you have the humility to learn the proper Clojure way. Sadly, most people would rather just try something and jump onto conclusions immediately.


Function definitions and vars are a form of state that can accumulate in a REPL session. Its not just atoms.

This can lead to issues where you decide to refactor and rename a var (Clojure is all about concise and expressive names, so its not uncommon to do this.. At least not for me) but you might not catch every place the old var name is being used. Because both vars are still exist in the state of your REPL, everything seems to work, but once you compile your uberjar or whatever, things are broken.

A REPL isn't a replacement for good unit tests, and there are techniques for reloading your REPL state on save, but the issue of needing to keep the "REPL state in your head" is real, and not an artifact of doing things wrong. Just something that the developer needs to be aware of as a potential hiccup in the REPL experience.


> If you write your code the correct way,

Then you don't need a REPL. This is a very weak argument, overall: there are programming techniques to compensate for any deficiency in the environment. The fact that you need to use them is already worse than not having to worry about it at all.

> Sadly, most people would rather just try something and jump onto conclusions immediately.

Just a gentle reminder, you're not talking to "most people" here. Nevermind me, we [EDIT: cut out the irrelevant details] I just want to say that assuming good faith and informed criticism are the better defaults for this forum.


Repl development is great for exploration, when you don't quite know the right representation of your problem yet.

The alternatives isn't changing your development environment, it's writing on a white board


Nobody said you "need" REPL. Doing REPL driven programming is just more productive, hence it is considered the right way in the Clojure community. Heck, you don't even "need" any higher level languages, you can write code in machine code, but that's besides the point.

Your boast about your Lisp credentials is exactly the kind of things I caution against in the article. Your pride has prevented you from properly learning Clojure, which has a very different programming style from other Lisps.

You are basically illustrating my point live, again.


> Nobody said you "need" REPL.

Well, you did, quoting the article:

> Yes, if you are not using the REPL, you are not doing Clojure right.

or should I understand this another way?

> but that's besides the point.

True. I'm saying that if you need to code in a particular way to make good use of the REPL, then it's already worse than being able to use the REPL well without any special coding style. Erlang and Elixir are great examples of that.

I also have a problem with you believing there even is one correct way to use any language, but that's bearable. You being so sure that you're the only one qualified to know anything about that correct way is much worse.

EDIT: I just saw so many "revolutionary ways to write code" which were being pushed hard by people absolutely sure their methods are so much better, and which ultimately amounted to little more than a few percent improvement in productivity in the real world - in the best case. It makes me suspicious of any such claims.

> Your boast about your Lisp credentials

My? I said:

> Nevermind me, but [...]

So you're putting words in my mouth; further, I said nothing about my knowledge of Clojure, so this:

> Your pride has prevented you from properly learning Clojure

Is completely unwarranted. And offensive. Could you please stop?


Then it is actually far worse than what I guessed. You know next to nothing about Clojure, but somehow feel that you are qualified to have an opinion about it. That doesn't look good, does it?


What opinion about Clojure?! Where? You're jumping to conclusions without any basis, while trying to caution others not to jump to conclusions?

I'm talking about the general ease of use of features in programming languages. If you need to code in a particular way to use a feature, then it's harder to use than if you didn't have to do this. Is that wrong? Could you provide any argument on why is it wrong? And yes, I use Clojure's REPL as an example, and give Erlang as a counterexample. Do you know Erlang? Are you able to compare? If yes, why don't you write about how you see that comparison?

Instead of, you know, childishly fixating on things I never said. For your information, I do know more than "next to nothing" about Clojure. But you won't believe me either way, will you?


I think this is caused because of the disconnect between the source in files and an environment in a REPL. There are alternatives:

You can keep your source inside your environment, like in Smalltalk. Whatever state you modified, it stays modified as long as you use the same image.

You can ignore hot reloading and have short-lived REPL sessions for testing and debugging. This works with Scala, OCaml, Idris, and similar. You don't need to track state too much, as it's frequently reset to known-good defaults.

Erlang allows hot reloading, but of whole modules only. You can only evaluate expressions in the REPL, not definitions. It's a good strategy, because the state in Erlang is rarely shared across modules, and having to reload the whole module ensures that it's always initialized correctly.

I think Clojure model is very close to what Racket has: you can switch between modules in the REPL and mutate their namespaces without ever reloading them from disk. Common Lisp is also close, the difference being that CL is image based, so there's a way for persisting the state changes... even if it's not the best of ideas :)

I like the Erlang model, but they all have advantages if used correctly. Pervasive immutability and sane reloading strategy help a lot, but even without those, you can use the REPL effectively. On the other hand, "living in the REPL" all the time is maybe going too far.


> but the longer your repl session is open and the more you change and compile, the more likely you are going to end up with a mixed state in memory that does not reflect how your program would behave if run from scratch.

I've seen this happen multiple times including during live Clojure talks at conferences. I also never found REPL driven development all that beneficial outside of quickly toying with trivial pure functions and that's possible in basically every popular language these days.


TL;DR -- Possibly it has to do with the style that I use, but those things doesn't actually happen to me very often, and when they do it's not a big deal. For me, the benefits of REPL-driven-development far outweigh occasionally having to deal with these issues.

Two things. First, I've been bitten by this myself, but it's pretty much always been something like "Oops, I renamed a function and forgot to update a reference to it, but the code still worked because the old definition was still there... until I reloaded the REPL." It's always been a trivial fix for me, since when I try to re-evaluate the buffer containing the outdated function reference it throws a "cannot find function definition" error. I currently consider the advantages of REPL-driven development to outweigh that occasionally happening.

Second thing: maybe others do it differently, but I don't think I generally make manual changes to the state while things are running. If the state doesn't look the way it should, I either start iteratively tweaking functions (by modifying my .clj file and eval-ing them) and re-running with fresh state to understand and resolve the problem, or I work with a separate, similarly-shaped piece of state and run it through functions in a sort of "manual unit testing" to tweak and fix whatever's wrong, and then I re-run everything from scratch to make sure it worked. I don't adjust the state, evaluate a code path, and then continue on if it works (because yeah, I can totally see how that would lead to the sort of situation you describe, which sounds unpleasant)


To me, that's similar to waiting to save your Word document until you have finished editing your document.

REPL should be used for rapid experimentation, but then the code should be transferred to a source file and saved before moving on to the next thing.


When doing REPL dev with clojure, I usually never type directly in the REPL, it's connected my editor and there are shortcuts for evaluating forms already typed in there (e.g. form under cursor, form before cursor, outer most form, entire file) and it outputs the results right after the form (as a temporary display or can even be output the text into the file).

There's also the handy "comment" function that lets you do something like (comment (+ 1 1)) in your code file that won't get compiled but keeps all the editing benefits (syntax highlight, structural editing) that you wouldn't get if an expression was commented out using comment syntax.


That is my understanding of REPL driven development, too.


This comment reveals a view of REPL for non-image based languages.

In clojure, we usually add forms (like functions) to the editor then eval them to update the namespace hosted by a separate REPL running in background.

Often the file is already saved before


>Why aren't we all writing in APL/K/J if it matters that much?

In all seriousness I think writing more code in APL would probably cut down on bugs, but array-oriented languages are so far beyond how most programmers think about programming, and look so obscure, that I don't think there's any hope of them seeing widespread adoption. Most people loathe point-free Haskell, so I can only imagine what they'd think of the APL family.


Not really; a direct descendent of APL is very widely used, and it's called Numpy. It's just that array-orientation, while great (especially for numerics), is insufficiently general to comfortably express arbitrary algorithms. You can write "APL" in many modern languages, but people still prefer to use traditional control flow for many purposes; hell, even APL provides this "escape hatch" (though it looks bolted-on).


Numpy is definitely not as powerful (read: concise) as APL, and you really can’t write “APL” in most modern languages. The operators are rank-polymorphic, for one.


Well I've had a few lesson in APL a long time ago and what I remember is that APL is a write-only language. So I doubt that it would 'cut down on bugs'


>Well I've had a few lesson in APL a long time ago and what I remember is that APL is a write-only language.

This is not how everyone who writes APL feels about it. It is recognized that it takes quite a while to really internalize how the operators work and compose, though. One advantage of APL stated by Aaron Hsu is that because the code is so small, if you find it confusing, you get in the habit of rewriting it frequently, and at least for him, it gets clearer over time. I have only dabbled myself (Primarily an OCaml and Elixir/Erlang programmer), but what understanding I have about empirical studies of software engineering suggests lines of code is more or less the only consistent predictor of defect count.


It becomes less "write-only" as you build up experience. It's just not as readily learnable (if you're already a programmer) as most other languages. I mean, procedural and OO languages seem to have largely adopted a variation of Algol or C syntax with roughly comparable semantics to others in the same family. A programmer in C, Java, JavaScript, Fortran, Ada, Pascal, Go, Rust, Python, Ruby, etc. can mostly jump to the others and be able to at least read the program without much difficulty, though learning to write and extend the program (especially idiomatically) will take more time.

APL's syntax and semantics are much different so it has a larger barrier for getting to the level of reading or extending programs written in it, but it's not impossible to reach that level with a bit of practice. I spent a few months doing APL for 2-3 hours a week in the evenings and I was able to develop a reasonable level of competence in it (though not so much I'd drop it on my CV), I can still read it pretty easily now a couple years later and without really touching it since then.


I also thought the bit about fresh graduates having no pride rather blanket. There are plenty of prideful fresh graduates with the kind of pride that experience hasn't yet tempered to produce a humble respect for the fact that you will die knowing next to nothing. Sadly, even some old timers never catch on to that.


> [1] Why aren't we all writing in APL/K/J if it matters that much?

Because most programmers aren't nearly as productive as we could be.

And because "concise" should be measure in the number of tokens needed to achieve certain functionality, not by excessive use of single character tokens.


I wonder if there’s a sweet spot for verbosity or if it doesn’t really correlate with bugs or productivity. I end up thinking about this pretty regularly.

For example Kdb/q has always fascinated me, my initial reaction was that encoding meaning into fewer symbols surely causes cognitive overload and promotes programmer failure, and yet i saw some python code written by a quant converted to q by a kdb developer. The original python was well written, it relied on pandas and numpy but all fairly garden variety stuff.

The q had exactly the same behaviour except some small changes for data ingestion. Around 500 lines of python, 16, not a typo, 16 lines of q. The performance comparison was absurd too.


> The q had exactly the same behaviour except some small changes for data ingestion. Around 500 lines of python, 16, not a typo, 16 lines of q. The performance comparison was absurd too.

I think this comes down to every symbol having a defined behavior that can be optimized around, rather than building a framework for creating your own symbols (functions). Dyalog APL has some similarly insane "beating C"-type benchmarks.

However, if you were to branch out into the more traditional constructs in Dyalog APL or J (K/Q have less of them due to being much, much less generalized), you'd probably see some significant performance decreases simply because they're more generalized. Eg. I doubt that Dyalog APL objects are as optimized as C++ objects.

Note: I have no experience designing languages or building compilers/interpreters. I just wanted to put my two cents in, rather than leave this on the somewhat vague and "mystical" note.


This has been my experience as well. 10x factor reduction in LOCs and increase in performance when moving to kdb/q


>And because "concise" should be measure in the number of tokens needed to achieve certain functionality, not by excessive use of single character tokens.

I am totally nitpicking here, but as a longtime Clojure programmer, I do have a bit of APL envy. Aaron Hsu (noted APL guy) made the point that brevity (in the linear, #-of-characters sense) let's you decrease complexity by avoiding indirection---when the body of a function is shorter than any name you might give it, you can just...write it...rather than writing it somewhere else and calling it.

I don't plan to switch away from Clojure any time soon, but I definitely want to play around with the APLs.


You may be interested in April[0], a version of APL embedded in Common Lisp so that it can be called as part of a CL program. Also, my own language BQN[1] is a rework of APL that comes much closer to Lisp in that it has first-class functions and closures. It's self-hosted (using a compiler that follows Aaron's strategy), allowing it to be embedded in other languages with relatively little effort, but currently poor performance. There are Javascript and C VMs with another programmer working on one in Erlang; Clojure could definitely be a possibility.

[0] https://github.com/phantomics/april

[1] https://mlochbaum.github.io/BQN/


Wow, this is totally cool, thank you!


> And because "concise" should be measure in the number of tokens needed to achieve certain functionality, not by excessive use of single character tokens.

That makes me wonder how this would shake out differently if we didn’t use Latin-based writing as the foundation of our programming languages. If you had a programming language that used Chinese characters, would that be fundamentally more usable, assuming that you understood Chinese characters to begin with?


> And because "concise" should be measure in the number of tokens needed to achieve certain functionality, not by excessive use of single character tokens.

What's the difference if the symbols were meant to have specific mathematical meanings and are really meant to be the only representation?

Plus, readability is subjective.


I think it goes further than the language's support of data literals to the culture of the language itself. The Clojure community takes programing with data literals to, what I would call, a pathological extreme. It's as if nobody wants to write actual code, just data structures to be interpreted, regardless of how general those data structures are. The problem is everybody gets hung up on code is data but doesn't realize that data is not code. At some point that data has to be interpreted, and passing around custom data literals trusting they'll be understood correctly elsewhere in your program turns into its own challenge.


You are contradicting what your parent post is saying and agreeing with the OP. So you agree that Clojure is the most data-oriented programming language, but you feel like it is too extreme?


That sums it up. I think Clojure is the most data-oriented programming language there is, but it gains that title not through features which other languages have as well, but through its general developer culture. And also, I do feel that the Clojure community often takes data-oriented programming too far.


REBOL and TCL are languages which go at least as far as Clojure in data-centric programming. Both feature incredibly elegant DSL for a variety of tasks, like constructing parsers or defining GUIs. Personally, I don't have an opinion on whether they (including Clojure) take it too far, but it's true that even that culture is not unique to Clojure :)


I don't know about REBOL, but I did program TCL extensively in my graduate school years. I agree TCL has a lot of similarity in term of treating code as data like in Lisp, but I do not think it has such an emphasis on data oriented programming like in Clojure. That's just not true.


It might not be immediately clear, but the "data oriented programming" discussed in this subthread is about using code as data to be interpreted. That TCL has way less primitive data structures and even less syntax for them doesn't mean it doesn't treat a lot of the code as data. REBOL[1] and Red, on the other hand, do have a comparable collection of data structures along with literal syntax for them.

[1] https://en.wikipedia.org/wiki/Rebol#Dialects


F# is a cleaner example of putting data first.


Does F# not put types first and data second?

Would it be idiomatic to do clojure style maps where the map keys end up being treated like pure functions that return a value.


>Does F# not put types first and data second?

The F# and ML view of the world is, roughly, that data and types are not separable in any way that's useful. Types are just how programmers make sense of data, and they exist in every language. The difference is whether or not the compiler will use them to help you make sense of your data as well, or whether you have to do your own manual type-checking.

As for programming with maps the way it's done in Clojure, I would say generally no, that's not idiomatic F#, and it's a big blind spot for F#. OCaml has row polymorphic records, which would be a better fit for the treat-everything-as-a-map style of programming in a static language.


Yes; I didn't mention it because, while they are technically literals, you are expected to write your own types and use your own constructors. But yes, F#, OCaml, Haskell, Idris, Elm, etc. are also very data-centric.


Great article. Even in a large enterprise, where the prevailing organizational ideology centers around building the largest headcount you can for political sway...

I've kept my team small on purpose, and we were able to be very effective by adopting clojure. We have sway because we deliver- and that's a different type of leverage than headcount. We don't get thrown every hot potato, instead we're consistently aligned with the critical portfolios.

A small dedicated team with tools such as clojure will outdeliver, and outcompete a larger team who cannot remain agile and require significant overhead to manage.

I'm basically rehashing PG's 'Beating the Averages', but it's been my experience as a dev mgr. http://www.paulgraham.com/avg.html


This totally makes sense in a startup context, but how does this work in a large enterprise? For instance, who signs off on "critical portfolios" being migrated to clojure, and who maintains these pieces when your team moves onto the next project?


First, if you haven't worked in a large "non-tech" enterprise, you might be surprised to find out it's just 100 startups with a common name and financing. The bigger the company, the less technical cohesion across teams. It's a blessing and a curse. I'm technically in a "business unit" as opposed to IT, so we're more focused on tangible KPIs. When you turn around innovative results in the biz domain consistently, no one really questions your tooling.

We started a new space Data Eng/ Data Sci and then it became critical. We've been able to maintain products by building long term platforms/systems that meet current and (anticipated) future needs in clojure. Initially, product and platform/system was all the same team. We've grown a little and now individuals tend to focus more on one or the other, but we're still <15 eng and shipping daily.

We maintain everything we've built to date, and we planned for that going in. Clojure's other superpower is that it is stable AF over long periods of time, handles refactors and testing well.

As for signoff in adopting clojure... our VP is old hat in clojure/lisp. We got lucky.


Because Clojure is not terribly fashionable, I imagine the developer pool is much smaller but the candidates are higher quality, mostly due to self-selecting. I really like the sentiment of empowering individual developers to the max.

It's funny, a lot of organizations want to beat the averages whilst engineering in an identical fashion to their competitors. You're not going to consistently get outstanding results if you do the same thing as everyone else.


>You're not going to consistently get outstanding results if you do the same thing as everyone else.

I think the tools matter a lot less than the people using them, and that programmers who perform above replacement level are rarer than is often discussed. I would take a great programmer writing the app in Java to a bad programmer writing the app in Clojure every day of the week.


> a lot of organizations want to beat the averages

Bold assertion! I think a lot of orgs would love to be as good as average, joking aside.


> You're not going to consistently get outstanding results if you do the same thing as everyone else.

I believe that they understand the premises the same way you do. So they can have 100 average developers or 10 great developers that are three times as effective. If your problem is one that no more than 10 people can work on, you'll take the 10 super experts. If it's not, you'll lose if you choose them because they'll be much slower.


> I imagine the developer pool is much smaller but the candidates are higher quality

People always say this about their favorite niche language and it always feels a little too self serving with little evidence to back it up. Learning languages is relatively simple and enjoyable, especially when the overwhelming majority of those people never end up working on difficult problems in said language. I think it often gives a false sense of superiority as well.


I can see what your getting at but I think this applies beyond programming languages. If you're interviewing someone who is into a niche programming language it can be a sign they are more interested in programming than say, someone who only knows Java etc. Obviously this won't always be the case and can lead to a weird sort of credentialing where people pretend to be into obscure languages.

It would be like if you were interviewing a chef who was saying "yeah yeah pan frying is fine but let me tell you about sous vide."

Then you go on to interview another guy who is into working the grill at chain restaurants.

Is the Sous Vide guy better? Certainly not always. But it can be an indicator this guy is a real nerd about cooking in a way that results in a better meal at the end.


Sure, it's definitely something worth taking into consideration, I just wouldn't value it too highly and in many cases I think there are much better indicators of ability.


>Learning languages is relatively simple and enjoyable, especially when the overwhelming majority of those people never end up working on difficult problems in said language.

Learning languages is not simple and enjoyable for everyone, especially not when they're languages with a radically different paradigm. I will definitely look twice at a JavaScript programmer who says they're also interested in ATS and Forth, for example. It should be fairly straightforward to suss out a rough level of understanding, and beyond whether or not learning languages is easy, it's a fairly strong signal of intrinsic interest, which a lot of employers want from their employees.


> Most people need some trainings to be able to get into something new. If they have not been taught REPL driven programming, I don't think they can discover it by themselves, despite maybe hearing others talking about it constantly.

Does Emacs count? I don't know Clojure, but I learned elisp for programming emacs and I discovered myself the power of evaluating any part of the code I write on the fly, experimenting with it. It's really useful in practice.


Emacs definitely counts given Clojure is largely influenced by Common Lisp and the fact that Emacs is the most popular editor for editing Clojure.


The description here of using the Clojure REPL for development is one of the best I've seen. I think part of the issue is nomenclature -- Read Eval Print Loop applies to lots of systems in lots of languages that many devs are familiar with that are not really what Clojure devs mean when they say "REPL." The actual experience is closer to something like Smalltalk where you alter a running system and experiment with it incrementally.


I guess I need to watch some of the videos he links, because this sounds horrible to me.

I mostly work in Python, which has a repl (though I'm sure not as complete) and sometimes I poke around in it. But if I am working on a real project, or even just a small script, I always want to run it from the beginning on every invocation, because anything short of that is not proof that it works.

"Editing a running system" sounds like a nightmare! How could you ever know things would actually work?!


> "Editing a running system" sounds like a nightmare! How could you ever know things would actually work?!

This is why Clojure developers love immutable data and pure functions so much. Say I am writing a web frontend for managing a list of users, and I want to implement some kind of filter and grouping flow on them for some business reason. Since I know the users are immutable, I know that when I am evaluating code in the browser to test out exactly what filter logic is correct I am not actually changing anything about the system.


I see REPL driven development more akin to writing music. I pick up the guitar and start playing. I can work on a small fragment and improve it until it sounds good to me. Or I can play the whole song. At all times I have instant feedback and I can hear what I'm working on.

In the case of the REPL you are playing your program. You can play a file, a function, or half of a function. Until it sounds right.

I lack the skill to use music notation for composition, so I rely on my instrument to give me feedback. And I lack the skill to execute the program in my head before I press compile, that's why I rely on the REPL.


How do you use your computer? A Unix shell is literally a REPL over a stateful filesystem.


Late to the conversation, but... I think unit vs integration testing is a good model for comparison. When I'm working in a REPL, I'm trying out individual functions to ensure that they provide the desired results (unit). Most functions are referentially transparent (if not totally pure), so this allows for fast/easy experimentation. If I want to test major code paths in a program, I can call "driver functions" or primary code paths to explore the results (integration). None of this is a substitute for real unit/integration testing, but it makes for some very rapid development. I worked in a lot of "REPL" (Python included) before I had the creme de la creme experience of using Clojure with Spacemacs and Cider. It's truly a different beast, and I'd withhold judgement until you've tried it (or something very comparable).


I do use REPL-driven development even for Python. (And why not? Like Norvig says, Python is basically lisp with traditional syntax.)

> How could you ever know things would actually work?!

Eventually you would run the project from beginning to end to make sure it works. REPL just allows you to by-pass that during development for rapid feedback. If the project takes 2-3 seconds to boot, we are talking about a difference of getting feedback in milliseconds vs several seconds - which makes a huge qualitative difference in developer workflow.

The point is precisely that you do NOT have to run it from beginning on every invocation. Doing it once in a while is enough.


I liked this video. https://vimeo.com/230220635

I had similar preconceptions from Python's REPL. Now after reading (at the least the first section of) the article and watching the video my mind is blown.

I've also been dabbling with Emacs Lisp recently and it appears that you can do basically the same thing.


It works much better when using immutable data and mostly side-effect-free functions as is heavily encouraged by Clojure.


It's a really different REPL paradigm.


What is? Clojure? Can you explain?


I don't use Clojure, but I've applied some of its lessons to my work in Julia.

The biggest one is the benefit of using simple, literal datatypes over classes/structs. If a function takes literals, you can just pick it up and run it, and if most of your code uses simple data types, it's more composable by default.

The mini-project I'm working on now has just one custom type, if I had written it 2 years ago it would probably have ten. This has made a big difference in the testability and reusability of my functions.


Pretty interesting topic. I am all for composability. However, I don't necessarily agree with particular benefits of pure data literals. I feel more secure when I wrap even simple types like Strings so that ItemId, UserId is a String but I cannot interchange them.

Similar level of composability with objects and structs can be achieved with generics and traits. I have a code that operates on arrays and matrices however particular implementations depend on the context - it is a DSL embedded into the application. For example I have `lazy matrices` that don't own the matrix or I can have matrices with owned data.


For sure. You can think of static types as deliberately preventing composability in cases where you definitely don't want it (e.g. passing in a user_id when dollars are expected), and that's often worth it.

The point about matrix types is interesting, because Julia does have a huge host of matrix types, which are primarily used for efficiency via multiple dispatch. Some examples are upper triangular, symmetric, diagonal etc.

One cool case is UniformScaling, which is an almost-matrix representing the identity times a constant. It's stored as just that constant, and can be used in 90% of the places a matrix can be used. Implementing these sorts of partial interfaces is one of the tricky parts of static typing, especially when you have multiple partial interfaces for one main interface. You can get around this with typeclasses a la Haskell, but that's a lot of overhead, especially if you have just one example of each partial interface.


Funny how a lot of "work" on large scale code bases boils down to de-coupling code from the datatypes they never needed to be dependent on (eg, function takes obj but only needs obj.foo)


It's a problem without an easy solution. There are some cases where you genuinely want to couple fields together basically all the time (e.g. a database driver, address, etc. into a "DB" object) but there are many more cases where you want to couple fields together 80% of the time. Those are the hard ones to deal with - separate your fields, and you need to do a lot of redundant work, but if you combine them that can be a pain to unravel and slow down your loops.

Julia's multiple dispatch is really helpful here, because you can e.g. write one connect_to_db that takes a DB object, and another that takes a string. I've found this flexibility to be surprisingly helpful, it completely takes away the tradeoff in many (not all) cases.


I'm running my own fairly successful self-funded business, largely thanks to Clojure and ClojureScript, which let me reduce incidental complexity and focus on what matters. So yes, a Clojure developer can do quite a bit alone :-)


I envy you :)

I can't get my head out from the Javascript ecosystem ... and previously I've done PHP ...

Those two made me think to quit programming, but lately I've found new ways like ClojureScript and ReScript.

After 15 years I came to a conclusion: choose a smaller and better language vs a popular one with a huge ecosystem.


Nearly every compile-to-JS language is a better choice than JS unless you really, truly need something legacy. The exception might be Coffeescript, but even that's not bad, it just add a whole lot. Even then I'm certain you can find a modern language that can target, say, ES3. I've never needed that, so I won't claim to know any off the top of my head. But it doesn't matter, because you could simply pass the output through Babel as a last resort.

Yeah, yeah another build step, but I consider almost anything as preferable to use Javascript or Typescript.

These days all my would-be Javascript projects are done with CLJS, Elm, or maybe PureScript if I want to do something fancy with types (or don't want to deal with particular Elm quirks).


Interesting mix :)

I wonder why CLJS / Reagent is not enough, and where the others step in...

I mean in my fantasy CLJS / Lisp is so powerful one never looks back to languages relying on commas and colons


Clojure uses colons for :keywords, and commas for unquote.


Eh no, commas are treated as whitespace in clojure. ~ is used for unquote


> I am not using the REPL much, am I doing something wrong?

Applies to python, too. I am always surprised if developers and data scientists working with python don't know that ipython, and by extension the python Jupiter kernels, come with a repl that hotloads code changes from local installs:

%load_ext autoreload

%autoreload 2

Is usually my first cell in a notebook.

Repls are so powerful. The repl like behavior of flutter is also why our mobile dev likes it so much.


I like Python, but the using the REPL w/ Python's indentation is not fun for me.


I'd venture a guess that anyone advocating REPL-based development in Python is pitching IPython/Jupyter. IPython's REPL supports multi-line editing, trims common leading whitespace from pasted input, among a whole host of other features. The standard REPL is definitely painful for all but the most basic tasks.


That's why you write code in your editor, and just ask your editor to send code to the REPL.


Why not just run the file? To be more specific, I get a lot of value out of Ruby, Elixir, and JavaScript REPLs because I can type right into them to try stuff out in one long, ridiculous command. If I have to type in a file, I'm just gonna run the file & print the result.


>Why not just run the file?

It's faster. That's the point of using a REPL.

>To be more specific, I get a lot of value out of Ruby, Elixir, and JavaScript REPLs because I can type right into them to try stuff out in one long, ridiculous command.

You can type long, ridiculous commands in a file too. Just clean it up as you go along.

I like to use my editor to send inputs to the REPL instead of typing directly into the REPL because different REPLs provide different editing experiences. Using my editor means all the editing features I like to use are always available.


Oh dear lord yes. Autoreload is my saviour in many respects.

I suppose that I come at this from R, where REPL (sortof) driven programming/analysis is a core feature, and until I discovered %autoreload I was feeling very left out in Python.

I guess I sortof understand why it is that way, but it definitely struck me as weird that one wouldn't allow code to be reloaded into a process buffer.


Serious question: Is there a way to use a Python REPL with multiple long-running asyncio tasks, some of which need a turn to run every X seconds?


I really enjoy reading anecdotes like the above. Programming languages are how we express intent to systems, and a complex enough domain where quantifying the tradeoffs is sufficiently difficult.

Teams can succeed or fail using any language, but there is a tangible factor somewhere between the language, available libraries and practitioners.


Serious question: is Clojure worth getting sucked into the complicated/verbose/nested Java ecosystem? I've avoided Clojure to avoid Java. Lately digging Rust...


IMHO.....no. If you're already a Java programmer or Android dev. the overlap with Java is a huge win.

However I remember watching a talk that I believe Stuart Halloway gave where he tossed out the statement "if you're not using java.util.Queue to help with concurrency you're doing it wrong." My initial reaction was to think that is brilliant and would solve some of my issues with Clojure, but after that came this feeling of annoyance thinking how the hell was I supposed to know that?! Java rears its ugly head more than Clojure programmers are willing to admit. And even then you don't get the full reach of Java, for example try getting JavaFX and Clojure to play nice together, good luck with that.

Faced with deep diving into the Java world to really get proficient at Clojure or looking elsewhere, personally I looked elsewhere and ended up over at Common Lisp.


It depends on what you mean by "worthing" it. To become a better programmer? Definitely worth it to know Clojure, as it does provide a new programming style that has inspired some other ecosystems. To find a job? Probably not. Clojure is not a popular language.

On the other hand, nowadays you probably can get away with not knowing Java when doing Clojure. There are also Clojures that are not on the JVM. For example, if you do Clojurescript, then you probably need to know some Javascript. There are also natively compiled Clojures for shell scripting, e.g. babashka, etc.


Yes, if you're an application developer that has to build the latest feature to come out of a spin-the-wheel product management meeting. E.g., you're tasked with adding arbitrary custom PDF functionality to your web app. For the majority of tasks, there are Clojure libs but if you need it Java has battle-tested libraries to do all of the tedious (/s) things that keep business-people happy. You can't assume the same for smaller languages/ecosystems.


Depends what you're into. I basically only use Clojure and can't remember the last time I had to know anything about Java. Admittedly I mostly use Clojurescript...

But seriously, I wouldn't worry. You'll have to install Java, and then you won't have to think about Java ever again if you don't want to.


How much can 2 Clojure developers do together? Is it more or less than what one can do alone?


My guess would be it largely depends on the devs in question. I also feel like this is true regardless of language.


> Clojure is also ideal for a startup, where a couple of competent Clojure programmers can write a complex application that would took a huge team of developers in other languages years of work.

There is a saying "if something sounds too good to be true, it probably is".

I wonder how many other languages did the author try to know that clojure is the reason for his success?

That being said, I really enjoy writing clojure and I absolutely love the interactive style of development. I hope more devs would try it so that it would get more traction in mainstream development.


Author here. I started programming in 80s (yes I am old), so I probably know more languages than people who are younger than me. I also taught programming in universities in my previous life as a professor. Like I said in my article, I won't gave up my corporate research scientist job in my 40s to start a company if I had not known Clojure, knowing that I would be the only one writing code at the beginning.


I’d really like to see a video of someone doing this REPL driven development. I’ve never come across one though. Has anyone?


I like this video. IMHO it is a good example of REPL driven development. Nothing to do with typing things into the REPL.

https://youtu.be/C-kF25fWTO8?t=1039


Javascript became easier after learning Clojure




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: