9 ms·
Clojure is cool
- z3phyr 8y agoSimilarly, I would whole heartedly recommend the Common Lisp macro system (having myself discovering how to use it a few days back). The power it gives to the programmer is unmatched by any other system I have ever used (except maybe forth)
- drcode 8y agoThe Clojure macro system is very similar to the Common Lisp one, arguably with some improvements- The Lisp languages in the Scheme/Racket family, on the other hand, have a different (more safety-oriented) approach to macros.
- flavio81 8y ago> very similar to the Common Lisp one, arguably with some improvements Quite the opposite. Common Lisp macros are totally unrestricted, they don't auto-qualify names with the package. Also, there are user-defined reader macros in CL, unlike Clojure.
- drcode 8y agoWell, auto-qualified names are a matter of taste. I agree 100% though that the CL reader macro system is a dream.
- squaredpants 8y agonil is not. nil punning is a band-aid. Otherwise, I love the lang.
- drcode 8y agoYeah, I think many Clojurians would mildly agree some changes to how nil is handled would be nice... but this is such a fundamental behavior that it's very hard to justify modifying the language at this point for just a minor benefit.
- james-mcelwain 8y agoOn the contrary, nil punning seems exceptionally well suited to a dynamic language. In general, I'm only burned by nil propagating into Java, almost all Clojure code seems to handle nil appropriately. I'm unsure of a good way to represent optionality in a dynamic language without a static type system. Or rather, doing so with the tools available -- core.match vs real pattern matching -- seems rather un-ergonomic. I'm curious about your thoughts of what would be better.
- squaredpants 8y agoYou are right regarding the dynamic language, well, dynamic. I haven't put any thought into it and I can't come up with a viable solution of eliminating nil without some type system strategy (Maybe/Either Monads in Haskell, Option in Rust, etc). It's kind of a bummer, because I love the Lisp dialect of Clojure, but really appreciate software projects that I've written in the past that don't crash due to a forgotten potential nil value handling. You do have a point!
- gnl 8y agoHave some monads and stuff: http://funcool.github.io/cats/latest/#maybe http://funcool.github.io/cats/latest/#maybe
- gnl 8y agoclojure.spec+generative testing help a lot, but yeah.
- squaredpants 8y agoI love the clojure community efforts, but clojure.spec is so confusing and bloat-y to me. :( It reminds me of Frama-C and specification of C programs, which isn't exactly what I would want to do all the time to have some safety guarantees on my program. I feel that a strong type system would provide way more benefits.
- gnl 8y agoMuch as I love Clojure, I agree that clojure.spec, while very powerful, has a mess of a UX. That's why – shameless plug – I wrote Ghostwheel [1], which, to me, turns it into a whole other thing, especially when gen-testing, spec-instrumentation and tracing are used together. Having inferred types in addition to this would be even better, but types are no replacement for generative testing or vice versa. They really complement each other quite nicely, but also have a large overlap in terms of how much they can reduce the need to do manual debugging and enable clean changes/refactorings with minimal effort. [1] https://github.com/gnl/ghostwheel https://github.com/gnl/ghostwheel
- ktosobcy 8y agoSo... awful and bloated Java code (with javadocs, why?!) compared to short counter-examples from Clojure. IMHO quite terrible way to compare the two…
- ivan_gammel 8y agoExactly. Java code looks artificially bloated (who uses 8 spaces for tabulation anyway?!). Class CookieMap is completely unnecessary, for example - it extends the j.u.Map with couple operations, which could be easily done with Java 8 streams (looks like it's older version of language - diamond operator is not used).
- 3rdAccount 8y agoThere could be a counterpart here that Java as a language is so complicated that people often come up with these design patterns because they THINK they are necessary.
- dnomad 8y agoExcept nobody actually writes Java code like this. Nobody implements the Map interface. It's just stupid code. The myth of Java bloat only serves people writing silly blog entries and others hung up on "best practices" from 15 years ago. It doesn't seem to have any practical basis.
- emidln 8y agoI've implemented the Map interface maybe a half a dozen times in my career in Java and many more in Clojure (using reify and friends). It's often very convient to offer a map interface to some remote service or data store as well as situations where I needed very custom caching or a specialized algorithm. The harder the thing I'm working on, the more motivated I am to present its API as something standardized if possible. This lets my users spend their complexity budget on what feature I'm offering and not on some random API I threw together.
- SomeHacker44 8y agoAnyone who writes Lisp without taking a moment to add a docstring to every definition would never get a job offer from me. (Not to mention comments as appropriate.)
- emidln 8y agoI don't think I would not extend a job offer for lack of a docstring, especially given how trivial most of these functions are paired with their self-explanatory names. I'd actually rather a namespace docstring explaining intended scope and api than function docstring for trivial functions. I've found that function schemas/specs and reasonable names eliminate 90% of docstring material. Whether the remaining 10% is worth the string above and beyond the fn name is a case by case deal. Comments are fine when the code is doing something unexpected or that is very terse. In other scenarios, comments are just a land mine to be armed when the code changes and the comment isn't perfectly updated. Bugs largely come from developer expectations being broken (mostly by one of: misunderstanding data shape, some API detail, some language feature, or miscommunication on the feature with the owner) and stale comments are a contributor to this which can be avoided in many cases.
- zonotope 8y agoClear and descriptive function names, concise and non-clever code, and a judicious use of well-named and concise helper functions are all way more valuable than docstrings. IMHO docstrings should be used as a last resort if the problem domain is truly complex enough to warrant it. Docstrings for trivial, well-named functions just get in the way, and if you feel the need to add a docstring to a complex function just after you've defined it, then I think that might be a hint that you should refactor the code to make it more clear and readable instead. An ns docstring that describes the intent of the api and docstrings for the major public interface functions are a good idea, but a blanket "docstring for every function" rule is a crutch to make up for unreadable, poorly-written code.
- jogjayr 8y agoIf you have to write a comment to explain every definition, you've failed to make the code sufficiently readable and simple to understand IMHO. Code-level comments can become outdated without eagle-eyed code-reviewers. The code itself can never lie. Comments are a useful for explaining the implementation logic of a function or method, for instance. Or for generating API documentation. And file-level comments explaining the purpose of the class or module are always handy.
- kristiandupont 8y agoLISPs seem to me the best candidates for an alternative to the traditional text editor. I could imagine a mind-map sort of view which would allow you to expand and collapse sub-trees etc. Does anything like this exist? To me, that would be reason to experiment with something like Clojure.
- cnasc 8y agoYou might find this repo interesting: https://github.com/shaunlebron/history-of-lisp-parens https://github.com/shaunlebron/history-of-lisp-parens
- gumby 8y agoThere is a long history (going back to the 1970s) of structure editors for Lisp and there was a discussion of them on HN recently. Interlisp and Interlisp-D were the primary proponents of this approach; I used to use D-Edit when I worked in Interlisp at PARC, though it wasn't really for me as it heavily depended on the mouse. IMHO structure editing emphasizes the wrong part of code development. In practice collapsing graph structure is less important in code as opposed to, say, JSON. Whereas you want to be able to use your eye to hop around, and free-form alignment sometimes helps to show parallel constructs. Remember also that in a structure editor even your comments have to be part of the code and can only be inserted in places where an expression won't change the flow of control (e.g. you can't do (if (* this is a comment*) some-condition result))
- swlkr 8y agoThis has a great explanation of the clojure REPL. I think a lot of people hear about the REPL and think, oh my language has a REPL too, no big deal. Maybe the clojure REPL should be renamed to something else to differentiate it from other language REPLs
- gumby 8y agoMaybe the other languages should rename what they are doing as Read Eval Print Loop comes from Lisp; read, eval and print don't have the same semantics in any other language (read especially).
- ronnier 8y agoThankfully Clojure will go nowhere beyond a few edge places. I saw a decent sized project written in Clojure script that had to be rewritten once the original authors moved on as new hires struggled to get anything done. Small features took enormous amounts of time.
- sergiotapia 8y agoLiterally interviewed at a company that is doing the same thing. B series, clojure/scala codebase, moving away to Elixir Phoenix.
- james-mcelwain 8y agoSeems pretty weird to suggest that if juniors are struggling with one niche language, moving to another would ever be the solution.
- pjmlp 8y agoFashion driven development.
- TeMPOraL 8y agoI believe that if a company wants to do software by throwing as much bodies at it as possible, they need to embrace it fully and openly. Going back to pure Java might be a good idea, as the language enables progress to be made in a "factory coding" environment pretty much by design. (Not a judgement; this is a legit use case, even though not an environment I like to work in.)
- dudul 8y agoLooks like a big waste of time and resources. They're probably just trying to use the "coolest" thing without worrying about delivering things.
- sergiotapia 8y agoYou would think but they measured what they need and they are sure that Elixir is the right fit for their use case. They aren't a CRUD company, like 99% of others. They are doing freaky things with multiple freaky orchestrations with multiple freaky OLD-WORLD institutions. Rails aint gonna cut it.
- jacknews 8y agoThe language may be cool, but the syntax highlighting scheme here is less cool, more 'vibrant'
- phakding 8y agoI like verbosity in programming language. It becomes pretty easy to read the code, compared to lambdas or other concise languages. If you are not working for a startup, majority of the time, you will be maintaining legacy code or bug fixing. I would take easily understandable verbose code over "clever" concise code every time.
- sfvisser 8y agoIn my experience ease of reading code is not a function of verbosity, but a function of familiarity. And in the general case verbosity and cleverness are orthogonal properties.
- phakding 8y ago> In my experience ease of reading code is not a function of verbosity, but a function of familiarity. You would think so, sure. I used to write Perl code and I was extremely familiar with it and it used to be pretty easy for me to hack a script. Going back after couple of months and trying to understand it though, was a totally different animal. You can argue that it was my fault for writing bad Perl code, but ask any old farts who ever had to write Perl.
- cutler 8y agoPerl is an elegant, artfully designed language based on linguistic context. If you don't grok it don't use it.
- ken 8y agoStudies have found that bug count is roughly proportional to program length, across languages. Saying you prefer verbosity essentially means you prefer more bugs. 500 lines is generally less understandable than 40 lines. There may be cases where terseness can be too extreme, but I don't see it here. Is there some particular aspect of the Clojure code here that you think is overly clever, or hard to understand? This Clojure code uses only one lambda, and in a straightforward way. I've written a lot of Java, and a moderate amount of Clojure, and if I had to place a wager on which version had fewer bugs, I'd definitely bet on the Clojure. Especially if there were the possibility that it was related to threads. We could write this in assembly language, and it'd take 50,000 lines, and probably have lots of bugs. The salient point is not (just) the lower line count, but that when code is shorter, that's a good indicator that it's written at a level of abstraction that fits the problem.
- Go0the0gophers 8y agoJvm is bloated. So no closure for me. I prefer Go
- yogthos 8y agoThe claims regarding JVM bloat are largely exaggerated, especially now that the JVM introduced modules. However, if it's not your thing it's worth noting that ClojureScript happily runs on Node. Here's an example of how easy it is to get up and running with https://github.com/yogthos/mastodon-bot https://github.com/yogthos/mastodon-bot
- dancek 8y agoI'm working on my first Clojure project and find JVM to be painful (it starts very slowly and eats lots of RAM). I'd appreciate any tips on making JVM non-bloated.
- jtr_47 8y agoLogically, don't use Clojure & the JVM if you want to avoid bloat. Try LFE, Lisp Flavored Erlang: http://lfe.io/ http://lfe.io/ or SBCL: http://sbcl.org/ http://sbcl.org/ Just a recommendation if you want to avoid the JVM bloat issues, which are real and have existed since the JVM was developed peace
- cutler 8y agoDon't underestimate Erlang's capacity for eating-up all the RAM on a machine in very quick time. Those lightweight processes start to add up if left unchecked. At least the JVM establishes an upper limit on memory use from the outset.
- fnordsensei 8y agoIf start time and memory usage is important to you, go with ClojureScript on Node instead. If you do stick with the JVM, it tends to trade memory for speed, unless you tune it otherwise. For startup speed, start it once, connect a REPL, and keep it running. A started REPL on a JVM instance can be flushed very quickly when you need a blank slate.
- jakeinspace 8y agoEvery few weeks I see a Clojure post on HN, and almost always there's a battle going on in the comments over whether the language is too clever, impractical, a toy, poorly designed, etc. My experience with clojure (a ~1500 loc library) has been quite good. I won't say it's intuitive for those coming from imperative/OO backgrounds, but when it clicked it really clicked. If a team decides to invest the time to learn clojure(script) for part of their stack I'd say it's a phenomenal tool. Please don't knock a language because of a team management failure.
- dustingetz 8y agoCode-sharing between ClojureScript and Clojure is a killer app
- saosebastiao 8y agoNot really. Although Clojure and Clojurescript are both dynamically typed, Clojure is strongly typed while Clojurescript is weakly typed. Clojure: (+ "1" 1) ClassCastException java.lang.String cannot be cast to java.lang.Number clojure.lang.Numbers.add (Numbers.java:128) Clojurescript: (+ "1" 1) "11" They may be close, but that is all the reason for concern. There are a million ways that small semantic differences like this can completely fuck you and leave you in a debugging nightmare. I would rather use javascript, AKA the worst language ever invented, than a language that claims to be cross platform but with semantics that change depending on the platform.
- hellofunk 8y agoI really like and use Clojure professionally, but I have become wary of the extraordinary time I spend dealing with runtime issues because of the dynamic typing. I hope the future of core.typed is bright. I know it is being very seriously worked on. It can't come soon enough for me. Nowadays, I prefer to write in any statically typed language even if it is more line counts, just for my own sanity.
- r00k 8y agoAre you using core.spec in your codebase? Did that help at all?
- hellofunk 8y agoNo, core.spec does not address the main issues with dynamic typing. It offers some neat stuff, but it is not a replacement for static typing.
- cutler 8y agoDude, Clojure is a dynamically typed language and always will be. If you don't like it use something else.
- hellofunk 8y ago> Clojure is a dynamically typed language and always will be I don't entirely agree with the "always will be," as Clojure is a very creative language that inspires a wide variety of experimentation, and core.typed is currently being actively worked on (as a PhD dissertation no less by the original author of core.typed), so there is ample room to think the future will offer good static typing abilities for Clojure. Also, I find your response unnecessarily curt.
- grovehaw 8y agoYou may want to check out Typed Racket as an example of what people can do if they decide they want types in their lisp. [0] [0] https://docs.racket-lang.org/ts-guide/ https://docs.racket-lang.org/ts-guide/
- haywood 8y agoClojurescript has been the real hero for me coming from a heavy Javascript background. The UI patterns are basically 1:1 with the functional standards in Javascript land, but you're working with a language that has immutable data structures by default (I can ditch Immutable.js) and the core library has all those goody functional helpers I'd include ramda or lodash for. But in addition to that, shadow-cljs is truly incredible, kudos to Thomas Heller. When developing, my editor is connected to the browser app through a repl, and I can switch namespaces and sort of TDD new code, or debug an issue by cracking into the actual pipeline and interactively spelunking. In JS land, everything is transpiled, so really you have to put debuggers, refresh the page or action, catch the debugger, and do stuff that way. If you're developing new code you can't test out a function if it uses some transpiled feature, so you write a test or do the debugger thing. It's takes some dedication to get there, and you don't need emacs even though it's really fun to learn and get proficient in. I use Spacemacs and am constantly learning some new package that's installed to help me. I recently switched from parinfer to paredit, and it's reaalllllly cool. With the repl driven development and structural editing you can achieve this kinda mind-meld with your development process. I don't think that necessarily makes this better than Javascript, but if you're into stuff like that there's a really high skill-cap with how you can optimize your development workflow. And really, at it's core to me Clojurescript is like my perfect Javascript. There were not any new concepts for me to learn as I had been programming Javascript functionally for some time, it's just everything I wanted in Javascript without the friction and bolt-on libraries. For developer happiness, it has a ton to offer, and there's always something else to dig in to.
- drcode 8y agoBTW I've been using the new figwheel-main testing features that were released a week ago and love them: https://figwheel.org/docs/testing.html https://figwheel.org/docs/testing.html
- sdegutis 8y agoClojureScript is basically an s-expression version of the good parts of JavaScript with lodash built in. It’s pretty cool. That said, if you don’t mind losing the benefits of s-expressions and macros, you can get mostly the same experience in vanilla JS. And even then there’s sweet.js which can get you part way there.
- sidcool 8y agoClojure is something I have been wanting to experiment with, but not having types makes it very difficult for me to reason while coding. It's just me. I am used to thinking in types.
- lostcolony 8y agoSo speaking as someone who has been on both sides of that fence (and, honestly, prefers static or optional typing; Dialyzer for Erlang is probably my favorite approach there), I think a large part of that comes down to how types are used. In a very OO language, where you're encouraged to create a complex type for every function/method contract (i.e., I have a type of RoomMeasurement, that internally contains a list of Measurement interfaces, each of which is in fact implemented as a MeterMeasurement, which wraps a double), static typing is very, very necessary, because it's not at all obvious what a function takes. And you need thorough API documentation because how another developer has chosen to represent things is not obvious (that is, you are trying to interoperate with a library that doesn't understand your RoomMeasurement, but does work with just a list of measurements, but they have to be in imperial, not metric, and how do you get the list of measurements from your RoomMeasurement, and convert them? Do you have to write a function, is there one already, does it take the RoomMeasurement, does it take the list of MeterMeasurement, does it just take a single MeterMeasurement? Etc) When sticking with simple types, though, it becomes a lot easier to reason about, and you can get away with just comments, or very slightly more complicated types. A list of measurements is just a list of ints...but maybe dropped into a tuple where the first arg is the type (i.e., roomMeasurements = {meter, [4.22, 5.7, 3.1]} ). And all you have to find/write (and since it's just data it doesn't matter which because there's no hidden stuff that needs tweaking) a meterToFoot function, and apply it as a map. I.e. (pseudocode), -> feet = {foot, map(roomMeasurements[1], meterToFoot)} Now, is that perfect? No; even if you as a developer choose to apply type information, another developer can choose to ignore it. But I think that's rarer; more common is when people don't think to supply type information at all, and pass around just (per the example), an array of doubles. You can do that in a statically typed language too, though. I think the key difference is that people coming from a statically typed, OO language, to a dynamically typed FP language, can either end up creating the same complex data types (which are a nightmare to deal with even with static typing, but doubly so without), or they see all the examples, embrace the simpler data structures...and then don't actually supply the necessary typing information. The reason, then, that I think static typing -is- good, is because it makes it harder for me to ignore/forget to handle the typing I have provided. That is, I may have a 'metricToImperial' function, that takes in a type tagged array of doubles, and a desired type, and determines and applies the appropriate function. But I can still forget to include a necessary conversion in the resulting case statement ('whoops, I called it with a lb to g conversion over here, and I forgot to implement that one'). It's times like that I really like optional/inferred typing; many places I don't need to check for typing, because it's obvious, both to the developer, and to the compiler if it can infer types...but I can still make mistakes, per that. Of course, that brings me to unit testing...
- shady-lady 8y agoDoes error handling not exist anymore?
- olieidel 8y agoFor web projects, the benefits provided by Clojure on the backend side may not be very large; there are already good languages with extensive libraries available (Django, Rails, etc.). It arguably may seem hard to choose Clojure with its minimalistic libraries when these frameworks provide an "easy" [1] way to get running with a full-blown admin interface. Furthermore, backend code by itself tends to be dependency-heavy in the way that you need dependencies which you'll definitely not write yourself: You need a library to interface with your database, you need something for cryptography / passwords, etc. Looking at the frontend (React) side however, things are different. The JavaScript ecosystem is a mess. From a viewpoint of a React developer, there are lots of libraries which vary widely in quality. react-router is an interesting example here, it had 4 (?) breaking changes so far by replacing the entire api. There's a ton of mental overhead for the normal React developer trying to write a "simple" app. Ironically, developers start rolling their own stuff. Instead of using a form library which tightly couples your components to your redux state (redux-form), you start writing your own. Instead of coupling your entire views to graphql via apollo, you start doing it differently, your way. This is where ClojureScript is a game changer. If your app differs just slightly from a (very) vanilla CRUD app and whipping some libraries together doesn't do the trick, you start writing custom stuff. When writing custom stuff, you want a programming language which is a) well thought through (great standard library, immutability, sane concurrency) b) predictable and c) productive. ClojureScript has all three while JS has none. We (Merantix) are currently developing a medical image viewer in ClojureScript and had prototyped two separate versions: One in JS with React, another one in ClojureScript with reagent and re-frame. Even though it is dependency-heavy (webgl stuff), ClojureScript turned out to be the superior choice: Immutable data structure at its core which ironically perform better than Immutable.js and way higher developer productivity due to less random bugs and a more interactive development (REPL). Using Clojure on the backend now seemed like an obvious choice: We can reuse and share code from the frontend and more importantly, all our developers are "full-stack" in the sense that everyone can at least understand what's going on "on the other side" (backend / frontend) of the stack as it's literally the same codebase. The learning curve is significant but the advantages are tremendous. I wholeheartedly recommend learning Clojure even if you're not allowed to use it at your job. It sounds cliché, but it will make you a better programmer for sure. [1] https://www.youtube.com/watch?v=rI8tNMsozo0 https://www.youtube.com/watch?v=rI8tNMsozo0 (to understand the meaning of "easy" above)
- Heliosmaster 8y agoI use clojure professionally exclusively over the last 4 years. It's awesome. I can't wait to go back and code everytime.
- zerr 8y agoOr just use Kotlin if S-expressions are not for you?
- amai 8y ago"Clear, readable, and concise code"?? I recommend everyone to once in a while try to debug Clojure code written by somebody else. Afterwards you will understand that this is a write once, read never language. It is an unmaintainable mess of overly clever recursive subroutines. It has some nice experimental ideas for concurrency. But basically all useful ideas are also available in Java nowadays. I wouldn't waste my time on it.