Clojure 1.6 released
github.com
github.com
The stock concern for businesses building with Clojure is the lack of talent and industry acceptance. I have a different angle. I figure that when I'm ready to start hiring other developers, I'll only be hiring developers sophisticated enough to see the benefits of using Clojure over Java/Ruby/Python/etc - and then I'll have the draw of letting them work in the best tools, not making them compromise so it's easier for management to hire less serious programmers.
(I know about Typed Clojure; it doesn't seem to have won the hearts of all Clojurians)
(Or, do you just test the bejezus out of the thing?)
(defn persist-this-item [item]
{:pre [
(map? item)
(= (type (:item-name item)) java.lang.String)
(= (type (:item-type item)) java.lang.String)
(if (:created-at item)
(= (type (:created-at item))
org.joda.time.DateTime) true)
]}
(let [item (if (nil? (:created-at item))
(assoc item :created-at (tyme/current-time-as-datetime))
item)
item (assoc item :updated-at (tyme/current-time-as-datetime))
item (assoc item :_id (ObjectId.))]
(mc/insert "tma" item)))
So this function needs to be given some kind of map, and it needs to have 2 keys, :item-name and :item-type, and both of these need to be strings, and if :created-at is already set, then it needs to be a Joda DateTime.It's not accurate to say that Clojure has no types. It just gives you some flexibility about how strict you want to be.
I find that Clojure strikes a perfect balance: its flexibility with types allows me to easily integrate a lot of 3rd party tools without much work, but when I need to I can be as strict as a I like with types.
Edit to add: wow, the code is ugly on Hacker News. Funny that this site provides no tools for posting code snippets, given the subject.
But that said, :pre/:post w/ unit tests seems pretty powerful. You can assign the invariants to the functions themselves and have a better / more robust set of assertions in your unit tests.
Yes one thing you might use :pre/:post for is type checking, but it can do more than just that.
Well, it would be cooler if the compiler could check those contracts at compile-time and issue some helpful warnings, but at runtime they are still valuable because the code will fail sooner rather than later. Plus you can probably disable them completely, should you experience problems with performance in production.
They also serve as documentation for other developers, documentation that you're forced to keep in sync. This documentation is not about the actual business logic, that ends up being laid out in tests, but rather about interface specifications and invariants.
So there you have it - testing serves a different purpose.
def increment(n):
assert isinstance(n, int) or isinstance(n, float)
result = n + 1
assert isinstance(result, type(n))
return result
So as long as we can agree on that much, then we can agree that there is a value to being judicious about where you should and shouldn't use type assertions.By the way I'm a fan of static typing as well, although I see value in dynamic languages too. And I definitely acknowledge the limitations of unit testing, although from a practical point of view, a comprehensive set of unit and functional tests is usually robust enough. And, of course, type systems don't make any guarantees against logic errors. :)
On your example, you're of course right. I'm also not a fan of checking the actual type in a dynamic language, since it defeats the purpose of it being dynamic. I like assertions that are more useful than that, like:
def sqrt(x):
assert x >= 0, "only defined for positive numbers"
last_guess = x / 2.0
while True:
guess = (last_guess + x / last_guess) / 2
if abs(guess - last_guess) < .000001:
return guess
last_guess = guess
Now clearly this helps, since it aids in readability (this function is defined for positive numbers only) and if you call it with a negative number, it will loop forever. Clojure has a variable called "*assert*" just for this
purpose: set it to "false" for production to
turn off all these type checks.If you're dead set on adding type declarations, perhaps you'd be better off with Haskell or something?
- its hard to safely refacture without static types (and the help of the IDE that often comes with it)
- dynamic languages must compensate the missing type checking support from the compiler with additional unit tests, negating the productivity gains
Sometimes it would be nice to have both worlds in the same language, but the way Clojure does it does't appeal to me at all.
The idea: add static types to Clojure without compromising on the dynamism / flexibility / convenience
Indent code by at least 4 spaces. (Just like in Markdown in e.g. Stack Overflow.)
like
this
and indent some moreIt's still saner than, say, Ruby.
For others, Clojure wouldn't help at knowing whats going on ;).
(:somekey (cheshire/parse-string
(slurp "/tmp/somefile.json") true)
Or (-> "/tmp/somefile.json"
(slurp)
(cheshire/parse-string true)
(get :somekey))
In other words, there's much nicer ways to write maintainable, readable code that you can still understand in the morning.Your "jumping around" comment also makes me wonder how you were laying out your functions? My advice is to go through the libraries of the popular Clojure libraries, you will discover a lot of good practices. It's taken me a few months of Clojure to start writing cleaner, readable code.
I have a friend who is a strong typing devotee who has worked heavily in Clojure for a couple of years, and he curses the lack of strong typing - but he's comparing to the likes of Haskell and OCaml. As a dynamic typing enthusiast myself, I'm comparing to Ruby, and Clojure is definitely superior in terms of how much it needs to be tested.
What drew me to Clojure over Ruby, though, was actually a matter of data modeling. I'm working with a graph database (Neo4J), and I found that good graph technique is terrible OO technique. I was violating the Law of Demeter constantly, reaching through objects to get to other objects, passing internal state around with the express intent of mutation, etc. Switching to a functional paradigm lined up perfectly with good graph design. No more smells! No more pain! And again, a lot less testing, because I'm no longer fighting against the nature of my data.
But if you actually have facilities to pass around and manipulate those kinds of data structures in a way with no side effects, it's trivial to verify (first on the REPL, then in your unit tests) that whatever transformation you're writing applies properly down the entire nested chain, and it will always work for anything of that pattern. And it is much quicker to write those kinds of transformations iteratively in a dynamic language than trying to represent it on a type level a la Scala.
I can't imagine having to go back to Java collection classes again. Ugh.
Strange...
I haven't used Typed Clojure, so I can't speak to that, but if you want to do type-level programming Clojure is probably not your weapon of choice.
On top of all that, sane defaults like immutable data, lack of mutable locals, and sane concurrency primitives means that a whole class of bugs just don't exist in normal Clojure code. All this sums up to a codebase that really doesn't need static typing.
I write Clojure code for a living, and have worked on some really large Clojure codebases, and yet I've never felt the need for a type system. It's a fallacy that large systems have to be so complex that only a type system will save you. Often the better route is to simplify and modularize the system.
Why are developers who have not worked with Clojure "less serious" than those who have?
In a typical Java shop or enterprise code factory, after 2-5 years you reach a plateau and the only way up is to become an "architect" or move into management. So that's what the better people in the mainstream Java world want to do, and ultimately the tenor is focused on "enterprise architecture" and Spring/Hibernate add-ons instead of making beautiful, powerful, or maintainable software and solving hard problems.
The Clojure community (I'm at Clojure/West as we speak) is full of people who (a) are really good, like top 1%, and (b) have at least the intention to be career-long programmers (in some capacity).
That's something you don't see in the Java shops. Yes, there are good programmers in them, but they usually are planning a move into management because the way most companies do software is limiting and puts you at an artificial ceiling after no more than 5 years.
Is it a gross simplification to make this about languages? Perhaps, but the empowerment that languages like Clojure give to individual contributors can be a game changer. It's reminiscent of what startups used to be, before the VC-funded ecosystem became MBA Culture's west coast colony.
Says someone part of the Clojure community. How does it feel to be one of these top 1% developers?
This kind of attitude is why the Lisp community has had a terrible reputation for five decades.
Claiming that a community you belong to is full of smart people doesn't mean you're claiming to be one of them.
But no, of course Clojure people are all the smartest programmers and are making all the biggest advancements in computer science.
I think it says much more about you that you read that into the post, than it says about the lisp community.
fwiw I only program as a hobby and while I think clojure is great to write in, it's a severe pain in the ass to figure out what the hell someone else was thinking when they wrote their code.
The problem with clojure and all lisps, as I see it, is that they make it easy on the author, hard on the reader. It's so simple to create your own special DSL that everyone does it. Now i have to learn a thousand languages.
This is a bit of a problem in Clojure because Lisp gives everyone the ability to play at being a language designer, but there's a reason that most language designers are extraordinarily experienced computer scientists: designing easily readable languages is hard.
Where, in my opinion, Lisp's DSL abilities shine especially well is when the Lisper is just taking a well understood domain with well understood terms and translating those terms into Lisp. Where Lisp's DSL abilities can shoot the Lisper in the foot is when the Lisper creates a DSL for a problem domain that's less understood, and where the terms used in the DSL will be less immediately obvious.
A good example of Lisp's DSL abilities really shining through is Hiccup, the HTML compiler. For instance:
(hiccup.core/html [:body [:div { :id "divTop" :class "headingcontainer" } [:h1 { :class "superheading" } "Welcome!" ]]])
is understandable because we understand the concepts of 'htmlness' 'bodyness' 'divness' 'h1ness'. We understand what it means for something to be a 'body' in the terms of HTML. However, when the domain the DSL is modelling isn't well known to the reader, then the DSL becomes significantly harder to understand than normal procedural code.
So yeah, Lisp can be a bit of a double-edged sword, but it's also possible to see how it acts as a gigantic force multiplier for small teams who understand their problem domain really, really well. This is why I believe it makes such a fantastic startup language: to write really good Lisp, you have to understand the problem you're trying to solve really freaking well, and that's a big part of running a successful startup.
Every library you use in any language is a DSL that you have to learn.
Lisps give you more power when defining a DSL, which means people can make them harder to use and understand, but it also means they have the power to make it easier to use and understand.
Library still have to follow the semantics and syntax of its language host. DSL does not.
I have attended the conj in the past and taken a bit of training in Clojure-based tools (Cascalog). These days, I am using Clojure for online programming challenges and various learning activities.
The Clojure community is like others - some people are very humble and welcoming. Some less so. But it feels like an open community in most cases and I say that as a true introvert.
My take on the idea of "top 1%" devs in Clojure is that there is a clear set of top Clojure hackers who are also invested in the craft of coding. It doesn't seem to be particularly ego-driven, but there are some incredibly hard-working devs in that group. I have observed a few, well-known names in that community invest a ton of time in reading academic papers, trying ideas out with Clojure implementations, and then quite publicly sharing the results. I think this was more what was being shared rather than chest-thumping of "oh, I'm at the Conj and you're not" perspective.
> I don't think they have any intention of climbing the management ladder because they didn't after two years
> I, arel, am in a good position to judge the quality of my previous coworkes, and they have all been fantastic!
Well, 'arel', first of all thank you for your useful contribution to hacker news.
Second of all, if in all of your 10 years in various companies (you also sound like you have been doing Java only, so this applies doubly so) you have never maintained any shitty Java code from a shitty Java programmer, well, you must either have been very lucky, or you must be one of them, don't you?
Some background just for you my friend: I've seriously programmed in (in order) C, C++, assembly, Java, Ruby and Python... and many other languages in between.
I've had to maintain, fix, performance optimise, make sense of badly written code in C, C++, assembly, Java, Ruby and Python.
Stop looking to demonise one set of developers and you will see that poorly written code and stupidity is everywhere, I can certainly see that.
Also thank you for creating an account just to reply to me. I'm honoured.
The same thing of course applies in the other direction; you can't tell what someone's intentions are from the fact that they're using Clojure.
Are you saying Clojure shops don't need architects? They don't need managers? Everyone just Clojures their way through "hard problems" and individually drives the business in new and more profitable directions? A language enables this?
Sorry, but claiming that a language is "empowering" doesn't somehow magically make anyone who uses it an uber genius.
Developers aren't a commodity and good developers don't want to be treated as a commodity. In the java world this means good developers don't stick around.
Companies that hire Clojure developers are frequently much more progressive about it (because if they weren't they wouldn't be using a language like clojure)
Average programmers don't learn Java because they're deeply interested in increasing the quality of their craft. They learn Java because it's what they were taught in school and/or because it will get them a job most easily. The bottom 80% of the programmer bell curve really doesn't give a shit about what kind of tools they use, as long as it keeps them employed.
When you get to programmers who really love programming, who want to write the best code possible and are willing to go out of their way to learn tools with very little chance of landing them that next corporate job, you're most likely in that top 20% already. So anyone who has learned Clojure, or at least can explain rationally why someone would want to learn Clojure (ie understands the benefits of functional programming), is probably not a benchwarmer idiot.
As a hiring founder who has absolutely zero interest in hiring below that top 20% (really, below the top 5%, ideally the top 1%), and has limited time to peruse a thousand basically identical resumes, applying a filter that gets rid of 80% of potential applicants is a Good Thing.
The point I was making was that there is a massive and grossly unfair generalisation solely about Java developers being made here and all over HN... that they don't really love programming, or want to write the best code possible. You might feel the language precludes that but then I would beg to differ and we are back to blub allegories.
By your own figures (which I don't agree with) the top 20% of a big number is a large amount of talented programmers you have excluded from your search because you cant get past the crud. Developers who have taste, know the JVM inside out and can write tasteful, readable, lightweight (yes) and highly performant code. Code that runs everyday in the big houses such as Google, Twitter, Linkedin...
I've interviewed many Java, Python and Javascript developers and our screening process helped to weed out the very hopeless, not perfect but most.
First a scan of their resume - is it generic, do they have experience in complex projects, a github account, a blog, contribute to open source, opinionated?
Second a real programming test that can test style, consideration of performance, typical gotchas. This should be automated so you can outright reject incorrect output... not fizzbuzz.
Other people more experienced on me have commented on just how bad the average programmers are at simple tests - across all disciplines.
http://blog.codinghorror.com/why-cant-programmers-program/ http://www.kegel.com/academy/getting-hired.html
I appreciate you don't have the time and you seek out developers who have challenged themselves with a more powerful language as your shortcut to find the elusive 1% but I imagine you'll find it is not insurance against poor architecture, algorithm selection, performance, unreadable code and applications that fall over in their first day.
The generalisations you and others make are unfair to a particular community of developers who just happen use a mainstream language (a sin in itself on HN) but care every bit as the rest.
But getting back to my original point way at the top of this thread, lost in the noise... I'm not using Clojure because I'm trying to put a snob filter into my hiring. I'm using it because it's the best tool for the job. I'm working with graph database and a graph data model. When I tried building it in an OO way (in Ruby, but I'd have the same problem in Java), I found myself violating the Law of Demeter and passing internal state around a lot. I felt I was choosing between good OO and good graph design - intermediate objects were messing with my architecture.
When I switched to Clojure and a functional paradigm, it all fell together beautifully. I feel very little friction between the requirements of the problem space and the requirements of the language. Right tool for the job, you know?
So what I'm addressing isn't a plan for hipster language snobbery - I'm finding the silver lining to the cloud of a relatively obscure language. Conventional wisdom avoids using non-mainstream languages because it's harder to find talent (read Crossing the Chasm for why). But in Crossing the Chasm terms, I want early adopters for my programming staff, not early majority. And Java is so mature that it's the choice of conservatives, much less early majority.
So maybe I'm not selecting for talent so much as personality type.
Seems like the key word here is "typical", and you risk drawing overly broad conclusions from your own experiences and/or the "conventional wisdom" one arrives at from reading too many forums.
I wonder how the software engineers who work at Google or Twitter and write applications in Java that "solve hard problems" feel about your assessment of their motives and goals.
Measured by what metric?
Spoken like someone who has never had to actually hire people! It's harder than you think.
I must admit that I was looking for a bootstrapping co-founder instead and even 50% market rate didn't make sense in that context so I don't know if they would've followed through, but it sure as hell didn't feel like a tough sell.
Once you get to 10 it's a very different ballgame.
So why doubt this same thing can happen again? It's like doubting that MD5 will ever be broken, or questioning whether something like Roman numerals can be improved upon. Of course it can, and will.
Unfortunately the song doesn't "keep it real".
If you're trying to find these developers through job postings I don't think you're going to have much success.
Your target market is developers passionate about Clojure so if you want to hire those you need to go to where they are; which most likely isn't Monsterboard.
At Clojure meetups there's a palpable sense of eagerness in everyone I talk with that isn't using it on a daily basis yet though and I would find it hard to believe if that didn't have a significant monetary effect.
As far as recruitment goes, especially in large numbers, I will definitely concede that it will be hard to scale up to large numbers without using traditional hiring channels and that those channels will most certainly fail you when recruiting this specific a skill-set.
I haven't had to hire for myself, but I've done plenty of technical interviewing over the years, and worked with countless programmers. Most just aren't the kind of material I would want to hire. The ones I would want to hire, even if they haven't used Clojure, would be excited about the idea and at least conversant in the advantages (and disadvantages) of functional languages relative to OO languages.
So it's making the job harder, but also easier.
The problem isn't necessarily sorting through resumes or giving good interviews. It's that all of the developers you want to hire have satisfying and rewarding jobs.
Here's my thinking, and I'd love some feedback on this. I live in Minneapolis, home of more Fortune 500 companies per capita than anywhere else on Earth (really!). These giants are full of very smart engineers that are frustrated and stifled by the nature of the beast they work in. I want to try to poach talent out of the giants by, among other things, offering a chance to work with technology that wasn't chosen by two years of committee meetings. And of course the speed, clarity of purpose, and control over their own destinies that comes with a startup.
This gets to another value of mine, and is going to play into my hiring. I want to hire people who have real-world experience in mainstream enterprise programming/ops. I'm building a product targeted at simplifying the lives of enterprise engineers, and I think it's very important that I hire people who have felt the customer's pain directly. I could get kids fresh out of school, but they won't have that experience.
Could you explain why the difficulty I faced in refactoring Clojure code is not issue for you?
Anyway, you don't have to believe what he believes; for example Brian Marick's Midje. (https://github.com/marick/Midje) Whatever makes you the best Clojure programmer is right for you.
By the way; I don't think Rich makes fun of TDD, but of how little of TDD is left when you're working with purely functional, side-effect free, code: passing functions data and verifying that you get back the right thing.
In practice every Clojure coder will tell you debugging is still a pain but that it's not a problem as long as you check each small, pure, function for correctness ... which is pretty close to, if not the same as, TDD.
----
Edit to subdue harshness... I wrote a huge ETL tool in Python using all pure functions, list comprehensions, only named_tuple, no classes, no mutation, lots and lots of garbage, but it worked and worked well. I took a two pronged approach to testing
1. Proved each pure function in separate file, this was for me, this stuff never got run again. It was like a nursery for pure transformations
2. Wrote high level integration tests, 1 or 2 per module (about 20 total)
3. If I had a nasty bug, I would set a breakpoint in the debugger (pdb) and duplicate all of the state around me as best I could and put it into a functional test that WOULD get rerun as part of the automatic testing cycle. I would love a tool that could extract program state and put it into a test for me.
4. The majority of testing was to run the ETL tool on subsets of the input and validate the output, I had a handful of these of increasing complexity. The output validation was automatic.
Testing a 4-10 line functions is waste once it is constructed. Higher level, whole module tests should cover an individual breakage of a smaller pure function. Tests are baggage (sometimes useful), just as static types are baggage (also sometimes useful). We need baggage, gotta wear clothes, read and eat.
Amen. I've wanted this so many times in Java. A really hacky way to do this is to serialize your objects while debugging and deserialize them in your test. The problem is if the classes change your test won't work because you can't deserialize anymore. Encapsulation and data hiding really get in the way here.
I don't like core.typed because what little research I've done shows that it actually becomes more verbose than java in some cases. That really seems like a step backwards.
It might also be that typing just isn't that beneficial for the code I'm writing.
I've watched Rich Hickey's talks and he makes fun of TDD as a design tool. And that's it. This doesn't negate the value of automated testing in general. Testing helps to ensure that the primary use-cases of your software system are fulfilled and to guard against regressions. When you get a bug, you write a test for it. When you forget about a business rule and find out you broke it later, you write a test for it. When you refactor code, it's very useful to have a suite of tests that ensure at least the main business logic still works.
TDD on the other hand is something different. Writing the test case first, before writing the code that passes it, always seemed like a dumb idea to me. So I always write my tests after and I'm not crazy about good code coverage either. But you do need tests for non-toy projects.
If you've not seen that before, it's seriously good. My top video of 2013
TDD is not designed to be a good technique for developing algorithm's but rather for designing OO systems and discovering the collaborations between various objects within a system. This doesn't mean you can forget about solid OO design principals and just somehow land up with a good design just because you use TDD.
So basically hire people that think exactly like you. That always works well.
Like many others pointed out, I do wish I had types though: refactoring is a giant pain in the ass even at our modest codebase's scale (10-15k lines), requiring endless UTs to make sure everything's still sane. Dynamic typing is fun for the simpler cases, but as soon as you go into trickier computations like crunching analytics data, you continuously run into run-time type mismatches that take forever to correctly pinpoint. Also having to continuously keep in your head just what exactly you're threading through functions is a pain and requires a lot of mental overhead.
The JVM also doesn't seem to bring much of a benefit to the table, considering that we only ever deploy onto 64bit Ubuntu 12.04, so there's not really a big need for portability.
The best part has been how fun it is to work in clojure and the community around #clojure channel with so many of its freakishly smart people. People on there seem to generally be very willing to help explain something if you're being obtuse.
[1]: http://en.wikipedia.org/wiki/Referential_transparency_(compu...
Or are you just referring to the fact that the core data structures are immutable?
Without the !, you can have some confidence that a given function is pure, since pure functions are sort of the "default".
To check if some elements match you need to use (some odd? [2 3 4]) - note the lack of '?'
Bet it will confuse people.
https://github.com/clojure/core.async Timothy Baldridge has a couple of videos on the go macro internals https://www.youtube.com/channel/UCLxWPHbkxjR-G-y6CVoEHOw
https://github.com/swannodette/om https://www.youtube.com/watch?v=DMtwq3QtddY&index=7&list=PLZ...
"e.g." means "for example"
Congratulations on the work.
Anything's possible, but Lisp has difficulty here.
Ruby on Rails was the spearhead of the whole convention-over-configuration paradigm, and getting back to simple, less expensive tools that stay out of the way. It's still not market-dominant if you get out of the startup bubble, nor are any of its followers. Java has ruled for over a decade.
Lots of them.
Before J2EE, there was a world of C++ with CORBA.
Why not just hand-roll on raw sockets like a REAL programmer?
To my knowledge, there are no app servers designed to run code written in C++. Certainly, none that are mainstream.
http://www.iis.net/learn/develop/runtime-extensibility/devel...
Funny thing about the way the company I worked for at the time picked up CORBA. One of our leads left for another company, was there about 7-8 months, couldn't stand it, and came back to us. But during that time he'd picked up CORBA at the other place and then imposed it on us.
That's what happens when you hire people back!
Additionally the project technology stack tends to be specified by the customers.
So I will only be able to use something like Clojure when:
- Everyone on the team knows it
- The customer asked for it
So is the life at the big boring world of enterprise consulting.
Clojure is a very practical language and basically stays out of your way during development.
Online - Clojure for the Brave and True, Clojure From the Ground Up are great series. Or try the ClojureBridge curriculum https://github.com/ClojureBridge/curriculum.
Practice problems - 4clojure, exercism.io, clojure koans
Editors - if you want to focus on Clojure not an editor, I'd suggest Nightcode or Light Table. If you like IDEs I'd recommend IntelliJ w/ Cursive or Eclipse Counterclockwise. If you already use Emacs, use CIDER. If you already use Vim, use Fireplace.
Congrats on the release!