19 ms·
Why Clojure?
- baldfat 12y agoHe won me at: > ... nor would I expect anybody to switch if they're happy with their current language of choice. For the uninitiated, however, these are the reasons why I love Clojure: Nice to see a humble post about the language they like without bashing any other language.
- netcraft 12y agoI'm hoping to pick up clojure this year. If nothing else just to have a new way of thinking about code - both from a functional perspective and a lisp perspective. I seem to find a lot of these articles - the only bad press i've seen about clojure is from other lisp'rs that think clojure isn't good enough. I do think it is interesting though that so many people love to extoll the virtues of immutability and yet have such a hard time putting those virtues into words.
- venantius 12y agoI had to think quite hard about how to express the value of immutability in this; did I communicate the point successfully? I'd love feedback on that part of the post.
- bmh100 12y agoYou did well, but it could be improved by giving an example of how mutability burned you in the past when trying to debug, or how functions are much easier to reason about, or even how significant state mocking code simply isn't needed (as I have heard is necessary in Ruby). In my own experience, I once built an OO Ruby application that interacted with an internal database to heavily process a CSV file. Dealing with the state of instance variables made debugging maddening. I won't say it had an excellent design, but dealing with shared state made things much worse. If it had been functional and immutable, I wouldn't have had to deal with many bugs I encountered.
- falcolas 12y agoDo me a favor, please do not write your configuration files in Clojure as well. As a non-Clojure developer, having to fight with Clojure configuration files for Riemann was a real pain point, and ended up deciding us against using Riemann for anything as a result (trying to convince a team of sysadmins that they would have to learn Clojure for a single piece of their monitoring infrastructure... didn't work). IMHO, you're welcome to enjoy your language to the fullest extent possible, I would just ask that you don't force others to use it as well. EDIT: Since it was asked twice in about 30 seconds, here's why having Clojure as a config option can be bad; it encourages things like this: https://github.com/guardian/riemann-config/blob/master/main.clj https://github.com/guardian/riemann-config/blob/master/main.... Please note that this is considered to be the gold standard by Riemann; it's directly referenced in their documentation. As for my preferred config file format? ini, yaml, json, in that order. They're simple, there are lots of tools out there to read and write them, and friendly to humans.
- whichdan 12y agoWhat format would you prefer instead? YAML?
- themckman 12y agoThe problem with using something like YAML to configure (if you even want to call it that) Riemann is that to do anything useful, you're going to end up inventing some convoluted "language", anyways. I'd much rather spend some time learning Clojure which I can take elsewhere than a custom YAML DSL.
- falcolas 12y agoYeah. After digging into it, I figured out that Riemann is more of a framework for building a monitoring tool than a monitoring tool itself. It provided powerful abstractions handling streams of monitoring data, but it was too unwieldy to be considered unless you started with Riemann and built out your environment on top of it.
- nickik 12y ago
- zirkonit 12y agoClojure is the most fun programming I've had in years. It takes quite some time to really kick in (more than one or two pet projects, rather than couple of "hello world"-style lines it took for Ruby), but once it does… walking up and down the ladder of abstraction never felt so good.
- venantius 12y agoI think the path to "Hello world!" ends up being a bit longer and the buy-in cost a little higher since Clojure isn't really a fitting language for scripts. As soon as you've got an application that requires more than a single namespace, though, the languages' strengths start to come into play.
- bmh100 12y agoIf Clojure was better suited for scripting, that would amazing. It's one area the language could be a bit better about handling. Setting a quick leiningen project to create a "Hello world!" certainly works and is relatively quick, but that includes a lot of leiningen baggage too.
- yogthos 12y agoI recommend taking a look at Pixie https://github.com/pixie-lang/pixie https://github.com/pixie-lang/pixie
- NhanH 12y agoThe main issue I have with Clojure is the stack trace ... If you've ever hate yourself when an error in Java popup and you have to trace 30 lines of stack trace in Java, it's far worse in clojure.
- venantius 12y agoThis is a completely fair point, though there are tools to help minimize it and/or to make them easier to read. I'm hoping to release something at least partly ameliorating on the subject next month.
- MBlume 12y agoHave you looked at this and did it help at all? https://clojure.github.io/clojure/clojure.stacktrace-api.html https://clojure.github.io/clojure/clojure.stacktrace-api.htm...
- NhanH 12y agoclojure.stacktrace is nice for production debugging from time to time, but it's not solving the problem: by the time the code get to production, it normally has enough log (and is reasonably in good shape) that you can figure out what's wrong. It's the development part that's annoying, especially when you're playing around on the REPL - you're not catching all the error manually, so all you get is the normal stacktrace.
- deleted 12y ago[deleted]
- bmh100 12y agoI find there's a trick to reading stack traces that actually makes them not so bad. If you focus just on lines that reference your classes, there aren't that many lines to look at. From there, you can usually see the point at which the error occurred and ignore all of the lengthy Clojure runtime lines higher up.
- terhechte 12y agoTry this, makes exceptions in Clojure much more bearable: https://github.com/prismofeverything/schmetterling https://github.com/prismofeverything/schmetterling
- fo0bar 12y agoClojure is fun but starting a Clojure program that just prints hello world takes over 1 second, even worse on a raspberry pi. I suppose command line tools are not its main purpose.
- yogthos 12y agoI recommend checking Pixie out https://github.com/pixie-lang/pixie https://github.com/pixie-lang/pixie
- mateuszf 12y agoIt's possible to use ClojureScript with node.js to get fast startup times, but one doesn't get jvm benefits that way.
- venantius 12y agoYou can make command line tools in Clojure, but in general I'd agree that you'd be better served by a language that can either compile and run faster [Golang] or is interpreted [Python, Ruby, etc.]. There's no real getting away from the JVM start-up cost, sadly.
- lunarprose 12y agoOkay, serious question: What is an example of a mid-sized project to really begin hacking on Clojure? Simple programs utilizing multiple namespaces for trivial reasons are great and all, but if I want to really understand what Clojure can do for me in a few weeks, what should I be trying to build?
- gdubs 12y agoMy first Clojure project was a simple chat server with multiple rooms. Built in a couple of days, and convinced me that it is indeed a powerful language.
- verisimilidude 12y agoMy first Clojure app fetched large gobs of interconnected data from a graph database (Neo4j). That data needed to be sliced into many different shapes. Clojure and its many standard functions made the task surprisingly easy and fun.
- BrianEatWorld 12y agoAs a tip to anyone who wants to give Clojure a try, be sure to grab something for your IDE to handle the brackets. For Sublime, I would recommend Paredit. Your sanity will thank you.
- hoprocker 12y agoI love reading articles like this about Clojure -- each one demystifies some part of the transition from another, more imperative language. (Here, for me, it was how to do unittesting in Clojure coming from Python.) Something the author touched on is the use of the REPL, in vim, to attach to remote running processes and debug. Does anybody have a quick example of how to do this?
- venantius 12y agoI use Tim Pope's excellent Fireplace.vim - https://github.com/tpope/vim-fireplace https://github.com/tpope/vim-fireplace
- hoprocker 12y agoI installed fireplace.vim when I set up Clojure for the first time (maybe 4, 5 years ago), but didn't really understand much else of Clojure at that point. I'll try it out again, thanks for the tip!
- MBlume 12y agoIn very broad strokes: The start-server command in tools.nrepl https://github.com/clojure/tools.nrepl https://github.com/clojure/tools.nrepl Will open an nREPL (networked REPL) server on some port of your choosing, by default on localhost. Configure your application to allow this to happen on startup. SSH from your dev box to the server, and forward the port the REPL is listening on https://help.ubuntu.com/community/SSH/OpenSSH/PortForwarding https://help.ubuntu.com/community/SSH/OpenSSH/PortForwarding (having the nREPL port only listen on localhost and then using SSH to access it is somewhat important for security. Someone else who connects to the nREPL port can run arbitrary code with your app's permissions, see any data being processed by your app, etc.) From a shell: lein repl :connect <port> From vim (with tim pope's fireplace plugin): :Connect nrepl://localhost:<port>
- ghodss 12y agoI have always wondered why, if functional programming is such a good deal in terms of better abstractions, entire classes of bugs eliminated, cleaner code, etc., it doesn't come near-universally recommended by the world's most experienced programmers for mid- to high-level tasks. I'm thinking people like Martin Fowler, Donald Knuth, etc., but especially people like Rob Pike, Russ Cox, Guido van Rossum, Yukihiro Matsumoto, who are all incredibly smart and experienced engineers who have dedicated their lives to developing non-FP languages. There must be some trade-offs to FP that are almost never make it into these kinds of "FP has dramatically improved my life" articles. (BTW I don't buy the "they're just not used to it" or "they're comfortable with what they know" explanation for this kind of people. These are not stodgy Java programmers who are working in programming as a day-job who are resistant to learning new things, they're people who know more about programming than a hundred average programmers combined and spend nearly every waking minute thinking about how to make it better.)
- michaelochurch 12y agoBeing smart doesn't make you a good engineer, and being a great language or library designer doesn't necessarily make you an authority on language selection for most engineering uses. Finally, take Pike and Cox and their work on Go. Go is a great minimum-change-for-engineers language for Google's purposes. Is it a great language for most? No. But if you're trying to introduce new concepts to thousands of C++ programmers at the same time, Go is a safer bet than trying to go full Haskell. To further answer your question, "functional programming" isn't always so well-defined. We know, realistically, that pure functional programming isn't going to work for all use cases. Once you're grounded in FP, you think of mutable state (or, in databases, destructive updates and deletes) as an optimization... but sometimes it's an optimization that you need. No language is FP-only because no language can be; even Haskell has the "dirty" IO monad. I think that most good programmers (like, 99%) recognize the importance of immutability and referential transparency, when possible, and in the function rather than the action being the standard compositional unit for programs. Where there is disagreement is on when, how, and how often to depart from the functional ideal.
- karshan 12y ago
- michaelochurch 12y agoClojure is definitely my favorite dynamically typed language. I also think that its community has a world-class aesthetic sense that other languages (looking at you, Haskell) could stand to learn from. Take maps as an example: Clojure has {"one" 1, "five" 5} and Haskell has fromList [("one", 1), ("five", 5)]. Clojure's syntax is a lot more attractive. Clojure has hyphens and Haskell has camelCase. Again, Clojure wins that one. Also, Leiningen is probably the best build tool I've ever used. I also think that Clojure has a great UX sensibility: once you get past That One Thing (parentheses, which are not as bad as they're made out to be) it's more readable than Scala and arguably Haskell. You can also make Clojure blazingly fast for a dynlang, although with some loss of aesthetics due to type hinting and array bashing. There are two issues with Clojure (3 if you consider static typing a hard requirement, because core.typed is brilliant but probably not "there" yet) and both come from the JVM. The debugging experience is still pretty bad; although I dislike debugging in general if it can be avoided, and that's why I've gravitated toward static typing (catch bugs early). The second is that the JVM itself imposes a ceiling on how well you can do performance-wise, and can require a fair amount of configuration in production. Of course, Clojure may be off the JVM in 10 years and, even if not, it's still a great language in very many ways.
- tomp 12y ago> Also, Leiningen is probably the best build tool I've ever used. Can you elaborate why? Is it also a dependency management tool?
- elwell 12y ago> Leiningen is probably the best build tool I've ever used Leiningen is like an Apple product: it's well-designed and usually 'just works'. But when something goes wrong you realize it's like a black box with only a mystical stack trace to help you.
- donmullen 12y agohttp://boot-clj.com/ http://boot-clj.com/ is definitely worth a look -- especially for clojurescript / web development.
- ClimbsRocks 12y agoGiven your experiences, would you recommend picking up Python or Clojure for data processing/data science? I'm currently quite deep into Javascript/Node, and am already comfortable with functional programming. Next I'm looking to gain more data abilities, and had assumed Python would be the natural next step. You're making me wonder whether Clojure would be a smarter step, though I am concerned that most of the jobs still appear to be in Python.
- venantius 12y agoIf I was starting a company - Clojure. If I was looking for employment - Python. Clojure lends itself very well to data processing and data science, but the data science ecosystem still isn't nearly as strong as Python's is.
- rakoo 12y agoAnd if you want to mix them, you can try hy (http://docs.hylang.org/en/latest/ http://docs.hylang.org/en/latest/): a lisp that compiles to the Python AST, allowing you to reuse every Python module under the sun.
- deleted 12y ago[deleted]
- fermigier 12y agoOne small nitpick: the supposedly "bad looking" Python example: SimpleTestCase(unittest.TestCase): def runTest(self): self.assertEqual(true, true) can be made more pythonic by using a modern testing framework, py.test: def test(): assert True == True
- vorg 12y agoSome people might find the example given of functional: (map (comp (partial g a) f) x) to be more readable as: (-> g (partial a) (comp f) (map x))
- stolio 12y agoFor those who don't know that's the thread macro and IMHO it should be a bigger selling point of the language. I think of it like the Unix pipe operator (i.e. "ls | grep X") but for programming. https://clojuredocs.org/clojure.core/-> https://clojuredocs.org/clojure.core/-> edit: I usually pretty-print it, too: (-> g ;; input value (partial a) ;; runs as (partial g a), hands off A (comp f) ;; runs as (comp A f), hands off B (map x)) ;; runs as (map B x), this returns a value or (-> 5 (< 10)) ;; runs as (< 5 10) => true with a little tom-foolery (lambda functions) you can have the incoming value land anywhere in the function's argument list
- fat0wl 12y agoheh i am always nervous to use this macro... while i agree it often makes things much simpler & more linear i feel like working without it would probably lead to quicker learning of how to mentally organize/dissect the normal syntax
- susan_hall 12y agoI love Clojure. Why? Partly because I am lazy. Larry Wall said a good programmer is lazy, but in my case I am lazy in the wrong ways. If you have enough self-discipline, then you probably don't need immutability. But immutability is a wonderful thing if you are lazy. I also love immutability because I have had many co-workers who are lazy. Again, not aways the good kind of lazy. I mean the kind of lazy that allows 2 loops with mutable variables into the same function: howMuchPrizeMoney = 0; arrayOfMoneyPerCategory =[]; for (i=0; i < users.length; i++) { u = users[i]; howMuchPrizeMoney += u.prize_money; } for (i=0; i < contests.length; i++) { c = contests[i]; for (j=0; j < categories.length; j++) { cat = categories[i]; if c.name == cat.name { arrayOfMoneyPerCategory[cat.name] = u.prize_money; } } } Wait, now I have a bug! All the categories have the same amount of money, and I know that is wrong. Where is that bug? Hmm, let me look and look and look. And if this example seems easy, I have seen functions larger than this, where finding the bug gets much harder. I am lazy, and my co-workers are lazy, and if we allow ourselves mutable variables, we will abuse them, so I would be happy if we adopted a language that avoided mutable variables. So for instance, even if I had a loop that looped over all the variables, there would still be no risk of one variable effecting another if I did something like this: (def vector-of-users [ {:name :henry :prize-money 200} {:name :henry :prize-money 30} {:name :craig :prize-money 340} {:name :craig :prize-money 100} {:name :pasha :prize-money 2330} {:name :pasha :prize-money 1130} {:name :pasha :prize-money 430} {:name :eli :prize-money 60} {:name :eli :prize-money 330} {:name :eli :prize-money 89} ]) (loop [u vector-of-users total 0] (if (first u) (recur (rest u) (+ (:prize-money (first u)) total)) total)) At the REPL, this gives me 5039. Now if I do this, and I stupidly put next a loop inside of a loop, is there any risk of a typo? Sure, but I won't get a confusing number back, instead I'll be told that I'm using a var that doesn't exist. This is the version without the typo: (def vector-of-categories [:housing :crisis :restaurants :theater]) (def vector-of-contests [ {:category :housing :prize-money 200} {:category :housing :prize-money 30} {:category :housing :prize-money 340} {:category :crisis :prize-money 100} {:category :crisis :prize-money 2330} {:category :restaurants :prize-money 1130} {:category :restaurants :prize-money 430} {:category :restaurants :prize-money 60} {:category :theater :prize-money 330} {:category :theater :prize-money 89} ]) (loop [money-per-category {} contests vector-of-contests categories vector-of-categories] (if (first categories) (recur (assoc money-per-category (first categories) (loop [c contests total 0] (if (first c) (recur (rest c) (if (= (:category (first c)) (first categories)) (+ total (:prize-money (first c))) total)) total))) contests (rest categories)) money-per-category)) When I try this at the REPL I get: {:theater 419, :restaurants 1620, :crisis 2430, :housing 570} But what if I made a typo, just like in the first example? What if instead of this: (first c) I stupidly wrote: (first u) There would be no "u" that was in scope for the whole of the function. The "u" would only exist inside the first (loop). Needless to say, no one would ever write Clojure code like this. Anyone who puts 3 loops in function, in Clojure, is taken outside and shot at point blank range. But my first example, in Javascript, is something I have seen in real life.