I've switched my own back end codebase to Clojure. It's just a joy to work with. Building REST with Liberator is a revelation. It's so easy to reason about what the code is doing, and immutability leads to far fewer bugs. Not missing objects, not at all.
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.
There's a story that I think I read in The Passionate Programmer, although I could be wrong about the source... anyway, the author was trying to recruit a team in India. He had a thousand basically identical resumes. How do you triage that? So he added Ruby as a requirement. It wasn't really a requirement, but it self-selected the resumes for those who take an active interest in programming, not a passive career approach. That got them down to a few dozen resumes to dig through.
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.
> 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.
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.
Really? I've found that it's easy as pie to find extremely highly skilled developers from my local Clojure meetup and get them interested at well below market salaries.
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.
For a startup, you don't need "many". You need a handful. If I'm trying to hire three programmers and they are make-or-break for the success of my company, I'd much rather be picky. I'll leave the hordes of generic Java coders to the people who have to hire a hundred at once.
You are vastly underestimating the difficulty of finding good engineers who you can work with. Sprinkling clojure in your job postings isn't magic. A world of difference exists between people saying "thats cool" and "I will work for you for 50% under market rates".
> Sprinkling clojure in your job postings isn't magic.
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.
I have no doubt about a tenfold variance across different people, but I'm highly skeptical of any claims that a particular language will make you 10 times more productive over the long haul.
People always doubt this, and then new languages always come along that improve developer efficiency. It was a lot easier to write a program in C than assembler, Perl than C, and Python than Perl, at least for me. I've seen some amazingly sweet code in some of the functional languages like Clojure and Haskell. My current main language is Ruby, and while it is not perfect, I would never trade it as a general-purpose language for assembly. I'm sure I'm well over 10x more effective than I would be if it was all assembly, all the time -- assuming I wasn't allowed to invent a new language as part of the solution.
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.
Clojure is not a 10x improve to over java, full stop. It's not even close, and I've done a project at my day job in clojure, it's my favorite language.
I wasn't drawing any conclusions; just sharing an anecdote and the impression it left on me.
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.
True. But what is hard is hiring good developers, not hiring developers that know Clojure. I found that any smart developer will have no problem adjusting to Clojure, especially given a history of any of #{Haskell, Erlang, Scala, Ocaml, SML}.
Still really rough. I have shipped two large production Erlang apps, smaller scale F# and Haskell apps. It is tough in three ways: (1) Just finding people who can do it, (2) finding people INTERESTED in using it, (3) affording people who fulfill the first two.
I made two hobby projects in Clojure and the biggest downside to it was the lack of typing (yes, I know of core.typed). Specifically, I found it very intimidating to refactor my Clojure code compared to Java. This isn't apples to apples because I write a lot more tests in my Java code than I did for my Clojure code. But, one of the reasons I went so light on the tests is because I've watched more than one Rich Hickey talk where he seems to make fun of TDD. I assumed he knew something I didn't and that automated tests would be far less important in Clojure than Java.
Could you explain why the difficulty I faced in refactoring Clojure code is not issue for you?
TDD is meant for supposedly helping with design. It does help, well sort of - it helps in writing code that is more testable. That's a plus, since the more you wait to introduce testing in your project, the harder it gets to do so. But you can't rely on it for the actual problem solving. Here's the proof: http://devgrind.com/2007/04/25/how-to-not-solve-a-sudoku/
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 your writing tests after the unit then you not doing TDD right.
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.
Did you use Prismatic's Schema [1] at the time? It seems like that was developed for your exact problem/use-case.
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.
Types don't prevent bugs (just classes of them) and small pure functions can still be composed into incorrect larger programs.
----
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.
> 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.
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.
Bingo! I did serialize data structures and put them in my tests. In my case it was Python so everything was serialized into a readable textual format. Maybe use snappy compressed json? Being purely functional and having a fairly flat data structures really helped, I didn't have to serialize very far down. Another side effect of being flat is the garbage collector has less work to do. Reallocate early and often!
No I haven't seen that, that looks pretty cool. It looks like a good middle ground between static types and core.typed.
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.
Not sure what Rich Hickey thinks, but my impression is he believes that example-based testing (http://www.infoq.com/presentations/Simulation-Testing) is superior to other kinds of testing. Also, he promotes design rather than only coding (typing); perhaps when you did less TDD, you didn't invest that time to do more design? (Maybe TDD was a time to do design?)
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.
Do what you need to do. There are more testing methodologies than there are stars in the universe. Tests absolutely help with refactoring, but they are also baggage.
> 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.
Why are developers who have not worked with Clojure "less serious" than those who have?
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.
Hi, I'm definitely not a top 1% developer and I use Clojure. Because I'm not a top 1% developer, I have found some success by picking tools that were a bit "on the outside looking in" and trying to essentially use these tools as leverage to be more personally productive.
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.
You completely read that into the post. At no point did the author include themselves in the one percent. They were commenting on that there are many programmers their that they consider one percenters in the clojure community.
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.
> 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:
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.
> It's so simple to create your own special DSL that everyone does it. Now i have to learn a thousand languages.
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.
FWIW, I'm at Clojure/west too, I've met a disproportionate number of outstanding programmers, measured by both knowledge and productivity, and I definitely don't feel like I'm in the 1%.
Claiming that a community you belong to is full of smart people doesn't mean you're claiming to be one of them.
If it were really the language of the "really good top 1%" why is it so easy to use? Why is it so popular? I'd expect a more hellish experience from smart people. Maybe that's the point of koans http://clojurekoans.com/ ?
Agreed. Every group you speak to always thinks they are somehow the smartest people on earth. The funny part is they don't seem to realize that with every other group believing the same thing, maybe their subjective opinions aren't quite enough to gain them the title?
But no, of course Clojure people are all the smartest programmers and are making all the biggest advancements in computer science.
2 years in a clojure shop here, previous years spent in .NET shops. I wouldn't say it's not exclusively the top 1% of developers. It's that you are more likely to find those top developers in a community like that.
I'm not top 1%. I'd like to get there but I'm not there yet. Part of getting there is being in a community where 1-percenters (relative to software as a whole) are 1-in-10 or 1-in-5 as opposed to the 1-in-100 (by definition) of the industry at large.
You've failed to establish why a company that uses Clojure wouldn't have engineers that plateau in 2-5 years and need to move into an architectural role or management role.
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?
I agree with this statement. For all intents and purposes, you could replace "Clojure" with "Brainfuck" in the GP's argument and it would make just as much sense.
Sorry, but claiming that a language is "empowering" doesn't somehow magically make anyone who uses it an uber genius.
I think he is saying the types of companies that use Java are typically big enterprise shops. And it's true that in general they don't have proper career paths for people who want to stay developers. Basically these kind of companies like to treat developers as a commodity.
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)
This self agrandizing is vomit inducing... Why in order to elevate your own community do you feel a need to assert vague stereotypes about some huge blob of developers that it sounds like you know very little about. All the Java programmers in my 10 years in various companies are in general fantastic and have no intention of climbing the management ladder after 2 (count em!) years in!
> I have never encountered any shitty Java programmers, so they must be pretty good as a community.
> 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.
Although it should be obvious, this extreme attitude is not shared by many programmers. I'll also mention that I've found Clojure programmers to be as friendly as any in irc and on the google groups.
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.
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.
It seems to me you can't generalize about what people are planning on doing based on where they're currently employed. Maybe it seemed like a good job at the time or there were exogenous reasons to take it or they didn't know what the options were or whatever---the fact that the institutional structure only allows for a few kinds of advancement doesn't mean that everyone at the institution is planning on that kind of advancement.
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.
Not forgetting all those stupid developers who produced the underlying ecosystem (core libraries and virtual machine) your wonderfully smart community is standing on the shoulders of. I guess they're off playing golf rather than solving "hard problems".
The average Java programmer isn't cranking out "ecosystem". They're slapping together a bunch of Spring components to make some dull business app while waiting for 5pm to roll around so they can escape the tedium of that dreadful existence.
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.
I understand your point... in terms of numbers Java does attract careerist programmers simply by critical mass. Although I would argue that language is actually now Javascript but thats another argument entirely.
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.
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.
I've done a ton of Java myself (used it professionally more or less since 2001). I understand there are a lot of fine programmers who are quite happy with it. That's okay.
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.
From a startup's perspective (my own), it's not so easy to find Clojure folks. Ever since I founded it I have less time to go to meetups (esp. specialized FP meetups) to recruit people, and most people that I interact with when I mention Clojure just say "What's that?" That's to say, so far I regard it as somewhat of a net loss that our backend is in Clojure.
I add :pre and :post assertions to most of my functions, so I know the type of everything coming in, and also what is returned. An example of a function that saves a document to MongoDB:
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.
Maybe I'm misunderstanding, but it looks like every time these functions get called, they're type checking their inputs and outputs? That seems like a waste of resources. Why not just write unit tests that ensure that they will always be called with the types you expect (or fail at an earlier step if not)? Identify the places where there might be ambiguity in something's type (e.g. when a JSON blob arrives, it might map a key to a list instead of a dictionary, or if a function might return multiple types, etc), and use unit testing to ensure that the type you expect will emerge from those places, or else a failure will occur.
Unit testing cannot prove the absence of bugs and shit happens in spite of their presence. That's why I still value (good) static type systems. But besides a good static type system that can prove certain properties about the code (and I'm not talking about Java's type system here), you can also do design by contract [1], which is precisely what @lkrubner is doing and I find that to be pretty cool.
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.
Nothing about what I was saying precludes the use of design-by-contract; in fact, that's what I was advocating. You say that "at runtime [type assertions] are still valuable because the code will fail sooner rather than later," which is what I meant by identifying places where there is a possibility for the wrong type to be used, and putting your assertions there. Sticking type assertions on the inputs and outputs of every function you write would be unnecessary, inefficient and horrible to read:
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.
I believe :pre and :post conditions only get run when assertions are enabled. There are other languages that have this feature (Eiffel touts it pretty heavily), but I haven't seen the AHA! example where this is the long lost feature I've been missing.
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.
If it only will get run in some sort of testing mode, that's fine I suppose. Although the other drawback to this sort of thing is that it seems to really decrease readability. Most of the time when you're reading code, you just want to see what's actually happening, and having a big messy set of type assertions would be noise 90% of the time (especially because unlike type signatures in Haskell, say, there's no syntactical difference that would allow syntax highlighters to help you visually see what's actual code and what's type assertions).
Right, but unit tests do that too. And that would still be something I would in most cases rather handle in unit tests than in assertions in the code itself, for mostly the same reasons.
How do you write a unit test that tries to assert that no one calls the function sqrt with a negative integer? Because that is precisely the case where pre-conditions can help.
You don't write unit tests to assert that a function never gets called a certain way. You can't, in a dynamic language: a function could conceivably be called with anything, and even in a statically typed language, there are some functions which cannot be determined at compile-time never to terminate without error. (For example, if you're using signed integers, the input could be negative). The goal of unit testing in this case is to ensure assert that if that happens, your code does what you expect it to, whether that's fail with a nice error message, return some default value, or simply bomb out. Depending on what context that function occurs in, it might be guaranteed to never happen (e.g. sqrt is only ever called by function foo, and foo always calls abs on its input before calling sqrt.). In those cases, it's not necessary to write those assertions into sqrt. You should separate what's actually subject to variability at runtime vs what can be ensured by program flow, and write unit tests accordingly. There's nothing wrong with the kind of assertions/preconditions described above, but I remain skeptical that they offer anything fundamentally stronger than a comprehensive suite of unit tests.
I agree that contracts don't offer something stronger than unit test. They offer something different. Of course you can't write the unit test that I asked and that was the point. But it would be kind of pointless too to write the test that checks that an exception is thrown when sqrt is called with a negative argument. It's trivial to see that happens by looking at the code. How sqrt fails is not interesting either, as you are not supposed to recover from that. A pre-condition is a way to specify that. It says: "Don't call me with a negative argument, just don't."
I agree, this looks terrible. It seems every discussion about static vs. dynamic typing on HN ends with the following realizations, spread over multiple comments:
- 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.
Frankly, Clojure doesn't need the intense levels of testing that OO languages require, due to immutability. There's a great deal less forking of possible states to examine.
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.
One of the really great things about having rich built-in data structures and a more-or-less purely functional data model is that it's really easy to express transformations and be confident that they work. In Java it's incredibly verbose to express & manipulate something like "a list of pairs of sets of integers" because if that was part of your core data model, you'd want to represent it in a class, and then you have all sorts of problems about what callers expect vs. what they get (e.g., do I get a reference to the original, or a copy? Can I safely mutate this?).
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.
That's a good point. A lot of what I built classes and objects for in other languages can really be expressed as simple maps, hashes, lists, and vectors, and more complex types built from them. Clojure's elevation of maps/hashes/vectors to first-class types puts it above Lisp, imho, and its functions to manipulate said data structures are better even than languages like Ruby and Python.
I can't imagine having to go back to Java collection classes again. Ugh.
but just in case someone considering clojure is worried about missing something like haskell's typeclasses (or whatever they're called in scala), it's worth mentioning that clojure does have some notions of "protocols" and "datatypes" that seem pretty similar.
They're not really comparable; Clojure protocols are a more flexible (runtime-extensible, for instance) notion of a (non-hierarchical) interface, and records / deftypes are just straight-up Java classes, with records having in addition nice map-style access to members.
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.
sorry, not well-phrased: i meant to say that, taken together, clojure's datatypes and protocols look somewhat similar to haskell's typeclasses, not that datatypes and protocols are similar.
Many times the clojure code is so much simpler, a) even basic testing will catch most of the bugs and b) there are less bugs since the code is so dense. In many ways it's just simpler to understand what 5 lines of Clojure code are doing, than 50 lines of C# code.
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.
FWIW: "What does this take and what does it return?" is rarely a question that comes up in Ruby for me. In most codebases, thats a pretty easy question.
For others, Clojure wouldn't help at knowing whats going on ;).
That was what drove me away from Clojure in the end. It's fun to write, but a challenge to write in such a way that you can read it later without jumping all over the place.
How big/complex were your functions? Have you seen the style guide? It's easy to write really powerful, nested functions in Clojure, which are damn hard to read the next morning. Taking advantage of thread-first and thread-last macros can really help here. I've finally started to use them heavily. For instance, you could do the following to read a file, parse it from json, and retrieve 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 had the exact opposite experience. I found that I was laser focused on a particular chunk of code when working in Clojure. Functions were logically grouped together. I never found myself jumping around.
I use prismatic/schema which can be used to define schemas and either validate on demand, or always-validate function inputs. Very useful and easy to add.
as a developer, it's harder to find a clojure shop close to my location. More clojure I use, less java/c#/php/python/ruby stuff I am willing to do. This makes interviewing harder for me too. I might just jump back onto non-hipster languages to make my life easier.
Been using Clojure in production for our API (core of the system) for close to a year now, it's been a very interesting ride.
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.
We've been using Clojure for our web app at work since day 1. The biggest gain by far is the referential transparency[1] built into the core libraries, which has also been closely adhered to by nearly all third party libraries (with only a few unfortunate outliers). I cannot emphasize enough just how much of a gain this has been.
Any posts which explain how the referential transparency built into the core libraries helps? My understanding is that Clojure is impure, so a "partially referentially transparent" design is interesting to me.
Or are you just referring to the fact that the core data structures are immutable?
Clojure strongly encourages referential transparency (e.g. immutable data structures), but doesn't demand it (e.g. STM). The convention is simply to mark any function with side-effects with a !, which almost all external libraries follow.
Without the !, you can have some confidence that a given function is pure, since pure functions are sort of the "default".
Not sure if I like the 'some?' operation. It's short for (not (nil? x)).. And has nothing to do with 'every?' -which checks if every element of a collection matches a predicate - ex (every? odd? [1 3 5])
To check if some elements match you need to use (some odd? [2 3 4]) - note the lack of '?'
I'd love to hear the reasoning behind that choice if it's Rich Hickey that did it, since it's called some and every in Common Lisp and he's written a great deal in that language.
The names were chosen by Rich. Many choices were considered and they all had pros and cons. The new some?/if-some/when-some functions follow the "not nil" notion in "some->" (but break with the meaning in "some"). Naming is hard. :)
The non-nil checks are particularly useful in core.async code as nil is a special value for channels (indicating they've been closed). This family of functions can be used to more concisely determine if a value from a channel is a value or means "close".
Absolutely loving Clojure. I typically hang out in java-land. I tested it out on an annoying problem to scratch an itch and wow, I'm actually enjoying programming again. I find myself looking forward to solving problems with Clojure in a way I haven't for a long time.
I'm in Detroit, and I'm not missing out on Clojure fun. Might miss out on meetups, but everything is still accessible from the Internet, so I don't see your point about NYC. With that said, a lot of different languages have beautiful code, from C, ie Lua source, PHP Symfony, etc. With that said, I love Clojure as well, and it's all because it's Lispy. :)
Like many people on the mailing list, I've been using the Clojure 1.6 betas and release candidates. Don't expect dazzling new features; rather, 1.6 is a nice solid step forward.
Really? I've gotten my fair share of recruiter emails looking for Clojure work. I introduced Clojure into my last job quite easily as well. If you're doing anything Java or JavaScript related, I've found it's pretty easy to get Clojure in the door once people see what you can do with it.
Interesting - where do you see these recruiter emails coming from, geography-wise? I watch, say indeed.com, and see Clojure mentioned mostly as "nice to have" mainly for what, on the surface, appear to be standard, enterprise Java jobs.
It's kinda funny how that worked out.. First, companies demanded employees write in C++, until employees demanded that companies start letting them use Java. Then, companies demanded that employees use Java, until employees demanded that companies start allowing them to use Ruby on Rails. Now companies demand that we use Ruby on Rails. All because we told them to.
I don't think the paradigm shifts happened because of programmer demand. Java's dominance came about because JEE (and J2EE) and application servers happened along at the very moment that the burgeoning internet and better browsers were driving architectural change. Firstest with mostest. Really, has anyone written a serious application server in C++? Add in massive enterprise support from IBM, Sun, and Oracle, and you get a winner.
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.
I had successfully forgotten about CORBA. Ah, the golden days.
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.
Maybe I'm being pedantic, but to me, an application server is something that you write programs for in a general-purpose programming language - that's how databases (where code is written in sql or something similar) don't qualify. Programs that run within application servers are written in langauges like Java and C#.
To my knowledge, there are no app servers designed to run code written in C++. Certainly, none that are mainstream.
It's a mix. For example, Staples Innovation Lab is trying to scale rapidly and hire as many as 300 people this year (https://www.youtube.com/watch?v=bmHTFo2Rf2w). Walmart Labs is here. Climate Corporation is here (recently bought by Monsanto). And lots of others as well, certainly including many small shops or consulting companies.
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.
There are ~9000 people on the Clojure mailing list and ~8000 members of Clojure meetups on Meetup.com. Hopefully those are useful and concrete numbers.
Yet, essentially all the web application examples I've seen have been very basic / boilerplate. Is Clojure not considered a good tool for complex web apps?
React was the missing link for sophisticated clojurescript web apps. The Clojure community discovered it in December. Expect substantial web apps this year. The largest open sourced example I'm aware of in this new wave of apps is Omchaya [1] which isn't that large but shows how large apps could be structured.
For a non-trivial example of a web application, check out http://www.dynamicalsoftware.com/software/architecture/oss/c... which documents the making of a news feed service that is written in clojure and combines lots of other open source technology such as postgresql, cassandra, solr, redis, and kafka.
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.