10 ms·
I'm sure Clojure is a great language for some tasks... But, looking at the examples (picked the Wordle one since I know that game): https://github.com/HumbleUI
by kpw94 2y ago
I'm sure Clojure is a great language for some tasks...
But, looking at the examples (picked the Wordle one since I know that game): https://github.com/HumbleUI/HumbleUI/blob/main/dev/examples/wordle.clj https://github.com/HumbleUI/HumbleUI/blob/main/dev/examples/...
I find it extremely hard to read. Even small snippets, say line 56 to 74 which define this "color", "merge-colors" and "colors"... then the "field" one lines 76 to 117 is even harder.
is it more natural read for people familiar with writing functional programs? (am I permanently "broken" due to my familiarity with imperative programing?)
I wonder what the same Wordle example would look like in, say pure Flutter.
Also wonder how would that code look with external dependencies (say hitting a server to get the word of the day), and navigation (with maintaining state in between those pages)
- trentonstrong 2y ago"is it more natural read for people familiar with writing functional programs? (am I permanently "broken" due to my familiarity with imperative programing?)" As just one person who has written a great deal of functional code, it reads well to me. I think because I am used to reading it "inside out"? Reading lisp-likes is probably helpful. Take 'color' for example. It opens with a 'cond', with three branches. First branch is if the idx-th position in word is the same as letter, return green. Second branch is if the word includes the latter at all, yellow. Otherwise we're grey. That took me a few seconds to grok. Just one anecdote for you. Don't think you're broken but reading/writing this kind of code even a little bit will change the way you see code IMO.
- exitb 2y agoThis is the function that confused the person you respond to, ported to Python: def color(word, letter, idx): if word[idx] == letter: return GREEN elif letter in word: return YELLOW else: return GREY I know which one I'd prefer to grok at 2AM with alerts going off.
- miroljub 2y ago> I know which one I'd prefer to grok at 2AM with alerts going off. At that time I'd just opt for sleep. Or sex. Or drink. Reading code doesn't belong to things one should do at 2AM.
- auggierose 2y agoAnd yet we've all done it.
- miroljub 2y agoNo. We didn't. At least not we all. That's just a myth spread by a few workaholic programmers. Luckily, there are enough 9-5 programmers to clean up the mess created by those 2AM committers.
- auggierose 2y agoI'll invoke a no true Scotsman argument here.
- tekknik 2y agonote how they said grok and not work? this is what oncall looks like, reading code at 2 AM
- JaumeGreen 2y agoThat's because you are more familiar with whatever style of code you are used to. Don't confuse familiarity with readability.
- deleted 2y ago[deleted]
- freilanzer 2y ago> I know which one I'd prefer to grok at 2AM with alerts going off. I hate meaningless statements like this. This means nothing, other maybe that you know Python. 20 years ago people might have said that about Python - I even know many people today who would say that about Python.
- thaumasiotes 2y ago> It opens with a 'cond', with three branches. First branch is if the idx-th position in word is the same as letter, return green. Second branch is if the word includes the letter at all, yellow. This is a tangent, but I've been thinking about how I feel when the conditions of an if-else ladder rely on the order they're listed in. This is an example; if you swapped the order of those branches around, the coloration would become incorrect. I'm a little happier when the conditions are described completely, such that swapping the order of the checks doesn't change which of them evaluate false or true, but it's also true that that can add quite a bit of complexity over an order-sensitive set of conditions. Thoughts?
- Pet_Ant 2y agoI mean if the checks are expensive and it's on hot path, then that's wasteful.It might also require then to use more nesting of IFs which isn't necessarily nicer.
- krferriter 2y agoHaving the order matter and matching the first `true` branch makes for more readable and less-wordy if statements. Otherwise when you have two conditions which have any overlap, such as A and B, for the A branch you need to add `and not B` and for the B branch you need to add `and not A`. This can create very long expressions. Having them evaluate in order and only match the first true one makes this unnecessary.
- Zak 2y agoYou could do something like this in Clojure: (first (filter identity [(and (condition-three) (action-three)) (and (condition-one) (action-one)) (and (condition-four) (action-four)) (and (condition-two) (action-two))])) And you could write a macro to do it with nice syntax. A bit more work and you could parallelize it. You probably wouldn't want to most of the time, but if the conditions are slow to test but otherwise inexpensive, it might be a useful optimization.
- deleted 2y ago[deleted]
- soerxpso 2y agoIt's readable, your (justified) problem is with his names. There's no language where "colors" and "field" will be descriptive function names that make it clear what's going on.
- koito17 2y agoI use Clojure professionally, but it's readable to me. I would personally encode the color merging rules differently since it looks too obscured as-is. But I think what doesnt help is the lack of types (or schemas) and the unhelpful function names, as described by another commenter. Also, (apply merge-with {} ...) is pretty "evil" in the sense that it's a very roundabout data transformation and would likely not pass code review at my company.
- matthavener 2y agoHickey talks about readability of Clojure in this talk (timestamp at 6m40s) https://www.youtube.com/watch?v=SxdOUGdseq4#t=6m40 https://www.youtube.com/watch?v=SxdOUGdseq4#t=6m40
- yen223 2y agoIt's just a matter of familiarity. If you showed me an article written in Italian I would struggle to read it, but that's not because Italian is inherently an unreadable language.
- Zak 2y agoAs an experienced Clojure programmer, I found that code easy to read. It uses quite a few idioms that are specific to Clojure or at least Lisp. Examples include cond, let [{:keys ...}], for being a list comprehension rather than a loop, #(%) function literals, and @ deref.
- tmountain 2y agoAlso found it easy to read even though I haven't written any Clojure in about a decade (spent a LOT of time with it when it was new).
- deleted 2y ago[deleted]
- dimitar 2y agoLet's see the features used in that snippet: * The cond macro which works similarly to C switch * Hashmap functions like merge and merge-with * Destructuring * The for macro which is similar to the "for each in" statements None of these are something unfamiliar to common programming languages so that code will not be hard understand once you go over the initial syntax and idiom hump. The syntax makes things much easier once you get to used to it, I think all Clojure programmers like it.
- vaylian 2y agoClojure is a lisp. Lisp languages represent code as trees. That's why you have so many parentheses. The trees contain language constructs (if, cond, let, defn, ...), data (numbers, strings,...) and names (function names, names of value bindings). There is also some more advanced syntax for quoting and macros. When reading lisp code, you navigate it like a tree. Indention matters and clean lisp code has the same indention level for all sibling nodes (with minor deviations for special constructs). Most code follows the pattern of defining "variable" bindings (e.g. via `let`) and then it has one final expression that uses all these bindings to calculate a value. (defn name-of-a-function-that-i-define [first-argument second-argument] (let [sum-of-some-numbers (+ 1 2 3) product-of-some-numbers (* 1 2 3)] (+ first-argument second-argument sum-of-some-numbers product-of-some-numbers)))
- ulbu 2y ago(i'll just make it clear that indentation matters to users as a strongly recommended convention for ease of reading. to the interpreter, you can write everything on a single line)
- vaylian 2y agoCorrect. This is not python.
- gorjusborg 2y agoI'll add that the related 'power' many Lisp acolytes talk about stems from the fact that everything is a list of lists. Due to this, you can write programs that take syntax (a list of lists) and modify that syntax to do something else (another list of lists). Imagine a language that has a built in parser and code generation library.
- CraigJPerry 2y ago>> I find it extremely hard to read. Even small snippets, say line 56 to 74 which define this "color” I think you make a great point, a point that once someone has gotten used to lisp, is harder to fully appreciate. I’m at the stage now in my lisp journey that i didn’t find those hard to read but it wasn’t that long ago that i felt almost nerd sniped by this weird language. I think it’s worth pointing out that in a more advanced example, I’d still have been comfortable because of the repl - I could navigate between each sub expression with a keystroke and I can send each to the repl with a keystroke and see what they do. Lisp really makes it easy to do this kind of bottom-up assembly - both when you’re writing and when you’re understanding someone else’s code. A corollary to that, and which was key to me falling in love with lisps, is that the signal-to-noise ratio is off the charts. Whatever you want to implement, probably doesn’t require a lot of code. Wordle in 189 lines is pretty decent. There’s just less to fit in your head and what’s there tends to be solving the problem at hand, not boiler plate.
- slowmovintarget 2y agoJust don't mention Electric Clojure, because that might cause some head explosions. (Fully reactive multi-tier programs where an entire web UI is 60 lines of code and implements infinite scroll with search and dynamic viewport resizing.) If you know Clojure, the code presented in the example seems fairly straightforward. Parentheses demarcate the syntax tree; and the last expression in any tree is the result carried forward.
- boxed 2y agoThe single space indent seems the most weird thing about that example...
- diggan 2y agoI've done a bunch of Clojure development professionally and don't find that code extremely hard to read. One thing to add to what others have said, when you're met with code like this in the wild and you need to modify/add/remove something but don't have a 100% understanding yet, the common workflow is that you explore this code with your "evaluator" (basically a REPL connected to your editor). So if you come across snippets of code you don't understand very well, you place your cursor at the various forms and execute them. So you'll start at the inner forms, and slowly work your way outwards, and after that you've verified assumptions both about the function's internal workings and how you'll use it from the outside.
- yas_hmaheshwari 2y ago>I wonder what the same Wordle example would look like in, say pure Flutter. Try this with ChatGPT, Claude or Gemini. All LLM's are really good with this translation tasks
- gorjusborg 2y agoIf you haven't used a Lisp-inspired language, yeah, it's going to seem different than all those imperative Algol-derived languages that have been popular for so long. I don't use Clojure professionally, but I've spent years using the language personally. It might be hard to believe, but it really is beautiful once you learn it, and extremely powerful as well.
- forgetfulness 2y agoThis one's pretty clean for Clojure code, due to the simplicity of the data model, in the most conventional sense, as the state of the program is just the word, the guesses, and the grid of colored characters for the guesses. External dependencies you manage like in most other applications nowadays, you don't hit external services in the "guts" of your code unless you really need to, for performance, testability and to keep the less reliable parts of your code isolated, you keep the interactions with external services as close to the "main" of the application as you can. When things break down is with more complex data models of the application, not even as much because of the language itself but because Clojure programmers actively reject using record types and interfaces, and just pass dictionaries around. You wind up with some code that, bafflingly, gives the impression of being very simple and neat, but you can't tell what it's actually doing.
- novagameco 2y agoLisp becomes easier to read if you have an IDE plugin that removes opacity from parentheses
- freilanzer 2y ago> I find it extremely hard to read. Having a bit of Lisp experience (really not a lot), I find it very easy and elegant to read. > is it more natural read for people familiar with writing functional programs? (am I permanently "broken" due to my familiarity with imperative programing?) No, most people who say something like this are simply unwilling to invest an evening into a language they're not already familiar with.
- ceronman 2y agoIt's possible to write some pretty unreadable code with Clojure, just like it's possible in any programming language. I can tell you that this code is very easy to read if you are familiar with Clojure. In fact, this example is lovely! Seeing this code really makes me wanting to try this library! Clojure has this terse, yet readable aesthetic that I like a lot. But I completely understand you because at some point Clojure code also looked alien to me. You are not broken for having familiarity with some style of code. Familiarity is something you can acquire at any time, and then this code will be easy to read. True hard-to-read code is one that is hard to understand even if you master the language it is written in.
- indigo0086 2y agonot going to say you're wrong or right, but learning lisps/clojure fairly deeply you can find worse examples. Also when people learn it their mind tends to find it a very consistent langauge overall visually. I've moved on after a long stint in clojure to elixir and while I like it, most other language pale in language consistency.
- dkersten 2y agoAs someone who’s written a lot of Clojure and have been using it on and off since 2009, this looks like decent quality code to me. I think it’s just a familiarity thing. Clojure is different from most languages in that it’s a lisp and it’s immutable-first functional. That gives it a bit of a learning curve compared to other languages, but I find other simpler languages quite dificulte to read until I’m familiar with them, too.
- jb1991 2y ago> I find it extremely hard to read. Even small snippets, And unfortunately, you won't get much compiler assistance either with Clojure, beyond basic things. So it's easy to have bugs that will take a while to track down in a complex codebase.
- iLemming 2y agoClojure codebases may be easier or more difficult to maintain depending on factors such as team experience, project complexity, and code quality. The ease of tracking down bugs varies across programming languages and is influenced by development practices and tooling. Clojure codebases are not more difficult to maintain than any other PLs. - Clojure has strong type inference, catching many errors at compile-time. - The REPL provides immediate feedback and testing capabilities. - Clojure's immutability and functional paradigms reduce bug-prone code. - Tools like core.spec offer runtime type checking and data validation. - IDEs like Cursive provide advanced static analysis and refactoring support. - Clojure's simplicity and consistency make bugs easier to spot and fix. - You also completely ignoring Clojure's rich ecosystem of testing frameworks and tools.
- jb1991 2y ago> IDEs like Cursive provide advanced static analysis and refactoring support. Can you give an example? Would these tools allow you to define a custom type with fields and ensure it is correct everywhere at compile time like a static language?
- iLemming 2y agoWhile Clojure is dynamically typed, tools like clj-kondo, Cursive and clojure-lsp can offer some static analysis benefit like warning about undefined vars or functions. There isn't "true" static checking, but you can use Spec and Malli for runtime checking. That doesn't provide same level of compile-time guaranties as statically type language, yet it offers some unique capabilities that many statically typed languages struggle to match, like - Dynamic predicates - Spec allows you to define types using arbitrary predicates, which can be more expressive than traditional static type systems; - Runtime generative testing - Spec can automatically generate test data based on your specifications, which is powerful for property-based testing; - Flexible validation - You can validate complex nested data structures and apply specs selectively, which is often more flexible than static type checking; - Extensibility - Specs can be added to existing types without modifying their source, and data-driven nature of it - Specs are just data and can be manipulated programmatically.
- erichocean 2y ago> I find it extremely hard to read. I avoided Clojure for nearly 15 years because I thought so too. Turned out I spoke English and couldn't read Russian. But that didn't mean Russian was unreadable—I just didn't know how. It had nothing to do with whether or not it was "readable" or not, it was easy to read (and understand) once I learned how. After about two weeks, I found reading Clojure to be just as easy as any other code. I did that at 46, so I don't think age is a major barrier. (I've written read and written code my entire life.) I'm now writing Clojure code every day and am much happier as a developer. (That's why I made the effort initially, and it definitely paid off.) One thing that really helped was asking ChatGPT or Claude to explain a piece of Clojure code to me, when I had questions. Especially early on, that was invaluable. Also, learning structured code editing made a big difference—I consider it to be essential. It was extremely frustrating until I spent an afternoon doing that. Clojure code is "read" differently than, say, Python or JavaScript or C and that's reflected in how you navigate and edit the code. YMMV
- grugagag 2y agoCan you expand on structured code editing?
- cess11 2y agoI think they mean when you learn the shortcuts for selecting and manipulating entire blocks between matching parentheses in an editor that helps balance them and so on, making it rather easy to test things out and refactor Lisp-like code.
- iLemming 2y agoStructural editing commands are like 'slurp' - swallows an expression inside another one; 'barf' - spits out a thing out of an expression; You can also do it from the left or right side. 'wrap' - wraps a selection into an expression; 'unwrap' - does the opposite; 'transpose' - swaps two expressions at point, and there are more commands. Once you learn the basic structural editing commands, writing code becomes like composing poetry out of haiku pieces. Instead of thinking like: "how do I grab these vars used inside this function and refactor it to be them in their own unit?...", you'd just grab some expressions and move them around, like bricks or lego pieces. It is extremely satisfying way of writing programs. The only drawback of that approach is that later it becomes harder to work with "more traditional" PLs, you just can't easily manipulate code the same way in Python, JS/TS, Kotlin, Java, C++, etc. - you need a "structured", homoiconic, lispy language for that trick to work. Treesitter makes an effort to improve the process, but it still not on the same level of simplicity.
- radix7 2y agoI've tried to learn Clojure a few times and just bounced right off every time. I found it impossible to read and frustrating and tedious to write. Everyone else who tries it seems to fall in love, but I really don't get it.
- tincholio 2y agoIt does take some patience, but once it clicks, it's just awesome.
- iLemming 2y ago> I found it impossible to read and frustrating and tedious to write. Perhaps you've done it wrong? To read any Lisp code one needs a REPL. And you don't typically type directly in it, you connect to it and eval things from source files. Once you get connected to a REPL, you can eval any expression and sub-expression, and with practice, you'd learn to grok the code without a REPL. And for writing Lisp, you only need structural editing support in your editor. Once you find basic commands - moving structures around is far more enjoyable process than writing things in an unstructured language. I am far more productive using Clojure instead of Java and Clojurescript instead of Javascript, Fennel instead of Lua, etc. - it's easier to read, easier to modify, easier to maintain. But, yeah, it does require some practice, just like any other skill.
- radix7 2y agoI am well aware of the benefits of a REPL, and find it pretty essential for learning any language. It didn't help me grok clojure any better, though. I'm not sure what you mean by structural editing support. I usually find things like autocomplete or automatic parenthesis to be more of a nuisance than a help.
- iLemming 2y ago> find it pretty essential for learning any language No, REPLs in other languages are not equal to REPLs in Lisp dialects. I bet what you are describing is not the same workflow that an average Clojurian would use. In other languages you typically type directly into the REPL console. With Clojure, you typically connect your editor to a running REPL instance and then manipulate things directly from the source code - you basically write the program, while living inside it - your codebase becomes a living, breathing, maleable entity. Structural editing has little to do with autocomplete, it's just a way to manipulate expressions - move them around, raise them, transpose them, wrap/unwrap, etc. I suppose you tried to understand Clojure by looking at the code, and that could be challenging - without proper REPL and structural editing support, it may not be the same joyful experience that many Clojurians know.