Native/Browser SPA versions using ClojureScript and re-frame talk at SCBCN17

Last month I had the pleasure of giving a talk at the Software Craftsmanship Barcelona 2017 conference about what my colleague Francesc and I have been learning lately while working on the mobile and browser versions of a SPA using ClojureScript and re-frame.

I talked mainly about how re-frame's effects and coeffects, re-frame's subscriptions and modelling a UI as FSMs place you in a pit of success, i. e., a design that, according to Jeff Atwood, "makes it easy to do the right things and annoying (but not impossible) to do the wrong things".

It was my first talk in a big conference and I had some technical problems, so I was very nervous at the beginning. After a while I relaxed and felt better. In the end I received more questions than I expected and very nice feedback.

I'd like to thank Modesto for lending me his computer when mine crashed just before the talk and helping me relax.

This is the video of the talk, (thanks Autentia for recording all the talks and for being so kind to me when I was having technical difficulties). I hope you'll find it interesting:

and these are the slides that I've improved after the talk using Modesto's feedback (thanks!!): .

Thanks to Francesc and all the people in Clojure Developers Barcelona Group, Green Power Monitor and Codesai that contributed to improve the talk with their feedback. Also thanks to my colleague Antonio for "persuading" me to give a talk in a conference.

Finally, I'd also like to thank Software Craftsmanship Barcelona 2017 conference organizers for giving me the opportunity to talk at such a great conference and for all the effort they put to make this conference better, edition after edition.

Permalink

A look at running Clojure on AWS Lambda

Johannes Staffans - A look at running Clojure on AWS Lambda

personal website and blog of

Johannes Staffans

A look at running Clojure on AWS Lambda

30.12.2015

Lambda is the name of the serverless function execution service that has been part of the Amazon Web Services ecosystem since late 2014. The evolution of the Lamba service has been quite rapid during the last year, with one of the recent additions being support for execution of Java code. And as we know, where there's Java, there can be Clojure, so during a recent project I decided to implement some of the backend parts using the AWS Lambda service.

Advantages

The obvious advantage of using Lambda is that you do not have to worry about server infrastructure, instead you are just deploying a function and letting AWS worry about scaling and so on. I think that having the function be the unit of deployment is a natural step to take when doing functional programming. Clojure of course works splendidly in this setting.

Limitations

One well-known limitation of running Clojure on AWS Lambda is the JVM startup time. This is a serious problem for an application that requires fast responses. In this case, you can still use ClojureScript running on Node.js, which is another execution environment that Lambda supports. If, like me, you can tolerate function executions that are sometimes very slow, Clojure works just fine. Lambda will also generally run subsequent invocations of your function using the same, warmed-up JVMs, so it's usually only the first few executions that are very slow.

Another limitation that one might hit is the 50 Mb size limit of the deployment artifact. 50 Mb might seem like a lot, but you can quickly reach that limit if you're not careful about avoiding pulling in heavy dependencies in your project. Amazonica is an example of a library that has a lot of transient dependencies. Liberal use of :exclusions in the :dependencies section of your Leiningen project file helps:

:dependencies [[amazonica "0.3.39" 
                :exclusions [com.amazonaws/aws-java-sdk-datapipeline
                             com.amazonaws/aws-java-sdk-devicefarm
                             ...]

One serious limitation that isn't immediately obvious is that Lambda functions have no way to communicate with private RDS databases. This is because permissions are granted to a Lambda function based on its (IAM) execution role whereas RDS access is security group- and therefore IP-address-based. The IP address of a Lambda executor is not known to the user, so you would have to open up access to large chunks (the whole published AWS IP range, essentially) to grant access to a Lambda function. Addressing this limitation seems to at least be on the AWS Lambda roadmap, but no dates have been given yet.

Practical issues

Deployment to AWS Lambda requires some command-line magic - I created a Jenkins job that runs Leiningen, tests the code and deploys a new version to Lambda. There are some things that need configuring, mainly how much memory to reserve for the function (this correlates directly with how much you pay per function invocation as well). I found 384 Mb to be the lowest possible amount for my relatively simple data-crunching function. Lower than that and the function would simply hang and time out.

Since Lambda is somewhat of a black box execution environment, logging is crucial. Logs can be found in CloudWatch, but you have to remember to give the function an execution IAM role that is authorized to create log streams - the docs have more to say on that.

Your application will probably also need some configuration, maybe in the form of an API key or threadpool settings (yes, threadpools are fine to use with Lambda). Lambda unfortunately lacks support for environment variables similar to what other AWS deployment tools like Elastic Beanstalk and OpsWorks have, so you have to get a bit more creative.

Usually, some setup that makes local development easy and while avoiding placing sensitive values in the source-code repository is a good way to go and I ended up using the immuconf library in combination with an S3 bucket. The blueprint for the configuration is a checked-in resources/config.edn file, which can be overridden by both a file in an S3 bucket and a file that is available only locally:

(ns lambda-example.config
  (:require [clojure.java.io :as io]
            [amazonica.aws.s3 :refer [get-object]]
            [immuconf.config :as conf]
            [clojure.tools.logging :as log])
  (:refer-clojure :exclude [get]))

(defn- base-config
  []
  (io/resource "config.edn"))

(defn- s3-config
  []
  (try
    (:input-stream
      (get-object
        :bucket-name "my-config-bucket"
        :key "production/config.edn"))
    (catch Exception _
      (log/warn "S3 config not available!"))))

(defn- local-config
  []
  (when (.exists (io/as-file "local.edn")) "local.edn"))

(def config
  (apply 
    conf/load 
    (filter 
      (partial not= nil) 
      [(base-config) (s3-config) (local-config)])))

Execution

Lambda functions are versatile in that they can be triggered by almost any type of event in the AWS universe. I opted for subscribing to an SNS topic, but you can also trigger functions based on for example changes in an S3 bucket or updates to a DynamoDB table.

The lambada library provides the necessary plumbing for actually executing your function in response to an AWS event.

Conclusion

AWS Lambda is a fun, versatile and cheap way of taking the functional programming paradigm one step further by having the function be the unit of deployment. Although there are some kinks such as the lack of environment variable support and restrictions with regards to RDS access, most problems can be worked around. The JVM startup time is a blocker for applications with real-time needs but not a problem for data-crunching background tasks.

Permalink

Action leads to inspiration

Johannes Staffans - Action leads to inspiration

personal website and blog of

Johannes Staffans

Action leads to inspiration

25.01.2016

Last weekend, a ClojureBridge event took place in Berlin. I was one of the coaches and had a wonderful time exploring Clojure with a group of new-comers to the language. In addition to coding, some lightning talks touching upon different aspects of software development in general and Clojure in particular were held. One of the presentations was by me, in which I talked about some of the hurdles that novice programmers might face and gave some tips on how to overcome them. This is that talk in blog form.

What does programming boil down to?

I'll take the premise that programming isn't something that is very hard to learn. It's not rocket science (unless your code is not running a website but an actual rocket!) or neurosurgery. Programmers regularily deal with simple math, basic logic and the syntax of programming languages - how you call functions, how data structures like vectors or hash maps are represented and so on. Once you start developing more complex applications, there are of course more things to learn, such as how the Internet works, the mechanics of different libraries and how to organize code in larger projects. But you actually don't need that much to get started solving smaller problems.

So what's the problem?

Despite the relatively simple nature of programming, it seems there is a general perception of software development as being something that's hard to pick up. Many people who start to learn to code seem to become discouraged at an early stage and don't continue learning. Why is that?

I think the issues beginners face when coding can generally be split into two categories:

  • Not knowing how to begin.
  • Getting stuck along the way and becoming discouraged.

Not knowing where to start

The step from thinking "I'm going to code a program that does X" to actually having running code can be intimidating if you are not familiar with programming. What editor should I be using? What framework can help me solve my problem? How on earth should I code so that the solution is elegant, I heard that writing spaghetti code is really bad?

There's a quote by Arthur Ashe, winner of three Grand Slams in tennis and recipient of the Presidential Medal of Freedom, that goes:

Start where you are. Use what you have. Do what you can.

My take on this is that you should begin your solution using what knowledge you already have. If you have only ever coded a Java program with a "Hello World!" string printed from a main() method, a Java program with a main() method is probably a good place to start. I'd interpret "do what you can" as trying to first solve the parts of the problem that you understand, such as printing out text or getting user input from the command prompt.

Your favourite search engine is also your friend when taking the first steps to implement a new program. You can start by typing in code from examples of similar applications, from tutorials or from screencasts.

Iterating in small steps is key - a common beginner mistake is to try to solve the whole problem at once. Always try to find the smallest thing you can solve first. If your problem involves working with a collection of values, first implement a solution that handles a single value. Work your way from the small to the large. There are some good books out there that can help you to learn this divide-and-conquer approach, such as "Programming Interviews Exposed".

Beware of bikeshedding (a term that gets thrown around a lot among software developers and basically means wasting time on irrelevant details). At the end of the day, what your program does is what matters, not which framework you use, which editor or which programming language.

Another favourite quote of mine, which I unfortunately don't know who to attribute to, is:

Action leads to inspiration.

A common misconception is that a programmer should know exactly what they're doing before starting to code. The solution should just appear before the mental eye in a puff of magical smoke and the actual coding is just the mechanical task of writing the program in an editor. In reality, I think it is often the other way around - we start coding first, without having much of an idea what we're doing. As we proceed, we see patterns, have new ideas on what we could go back and change in order to make it better and thoughts on what we should concentrate on next. In short, taking the action of starting to code leads to inspiration about how to solve the problem at hand - not the other way around.

A programmer will often find that the inspiration that comes from coding will take the solution in wholly new directions that could never have been anticipated when starting out. Often this can mean throwing out heaps of existing code and starting afresh, which I see as a good thing - the first attempts at a solution are usually just about gaining a deeper understanding of the problem. Don't become too attached to your code, a lot of times throwing it out is just the right thing to do.

Getting stuck

Getting stuck and not knowing how to proceed with a particular programming problem is maybe the biggest source of discouragment for new coders. It is unfortunately something one has to become used to, as this is something that happens very often, even to experienced programmers.

The scary thing about getting stuck coding is that it can feel very personal and intimidating. In other occupations, one might get stuck because a tool breaks, or a shipment of goods is late, or something else that is out of our control. In software development, it's easy to think, "I should really know how to solve this problem!", or something along those lines. Programming is after all primarily a mental excercise.

Once down the rabbit hole of feeling inadequate, it's difficult to get out. The primitive and irrational part of the mind, the limbic system that produces emotions like fear and anger and triggers fight-or-flight responses, is in full control and does not give the slow, logical prefrontal cortex a chance to solve the problem. Daniel Kahneman's excellent book "Thinking, Fast and Slow" explains this mental process, modeled as two systems that drive the way we think, in fascinating detail.

To get un-stuck with a difficult problem, you need to give your brain time to process the information. You can for example go out for a walk, watch a movie or go dancing - anything! For me, solutions to problems I've faced during the day often come to me just before I go to sleep. Having a notebook by the bed may be useful if that's the case for you, too.

Novice programmers can often feel intimidated by more experienced developers, as these seem to blaze out code without much effort, taking every problem in stride. You should know, however, that a programmer like that has probably seen similar problems hundreds of times already and has developed a repertoire of patterns around them. Recognizing patterns and having a gut feeling about how to approach them, what data structures to use and which algorithms are a good fit is something that comes with experience. Once you start building your own repertoire, you'll start seeing patterns too and know instinctively what solutions to apply.

Conclusion

Software development is fun and rewarding, but can also be frustrating, particularily for inexperienced programmers who don't know how to start approaching a problem. Getting stuck can be intimidating but is something that I think new programmers need to become friendly with. I hope this post can give some ideas on what actions to take to get that inspiration flowing!

Thanks again to the organizers of ClojureBridge Berlin!

Permalink

Direct Higher-Order Function Calls

When optimizing performance-critical ClojureScript code, function call overhead might be important to you. By default, when compiling ClojureScript in :advanced mode, the :static-fns compiler option is enabled. This post describes an additional option, :fn-invoke-direct, which is an extension to :static-fns for higher-order functions.




As background, it might be worth reviewing this post, which describes :static-fns, giving a concrete example of what it does to emitted code. Additionally, Static-Free ClojureScript REPL gives a concrete example illustrating why it is good to have :static-fns disabled by default for the REPL.

The :fn-invoke-direct compiler option was introduced with the ClojureScript 1.9.660 release. Let's explore this option by way of an example.

Consider the function

(defn f [h x]
  (h x))

where h is a higher-order function, and we want to focus on the code emitted for applying it to x.

Incidentally, Lumo and Planck support the same command line options for enabling :static-fns and :fn-invoke-direct. This makes it really easy to play along at home, trying functions and seeing what code generated. For either, -s / --static-fns enables :static-fns, and -f / --fn-invoke-direct enables :fn-invoke-direct.

If you look at the function body generated for f with :static-fns disabled, you will see that it is

return h.call(null,x);

Note, to see this, you can enable verbose mode for your REPL, or alternatively (set! *print-fn-bodies* true) and then evaluate the function f in your REPL.

If instead you enable :static-fns, you get

return (h.cljs$core$IFn$_invoke$arity$1 ? h.cljs$core$IFn$_invoke$arity$1(x) : h.call(null,x));

This looks a bit more complicated, but all it is really doing is this: If the passed function h happens to have been defined as a multi-arity function and defines an arity for one argument, then it statically invokes that implementation. Otherwise it falls back to what you would get without :static-fns enabled: h.call(null,x).

That will result in faster runtime performance in the case where passed function looks like this:

(defn g
  ([x] (* x 2))
  ([x y] (* x y)))

If we benchmark a call (f g 2) under :advanced with both :static-fns disabled vs. enabled, we get these speedups:

V8: 5.6, SpiderMonkey: 1.8, JavaScriptCore: 2.6, Nashorn: 5.2, ChakraCore: 5.1

This is done using (simple-benchmark [] (f g 2) 10000000).

Now, what if instead g were not multi-arity, but looked like

(defn g [x]
  (* x 2))

Note that it is essentially the same, as far as f is concerned, which only invokes h with one argument. In this case enabling :static-fns can't provide a speedup.

In fact, for this scenario, the simpler code generated with :static-fns disabled runs faster under most VMs. Here are the same speedup benchmarks observed under :static-fns with this new definition for g:

V8: 0.42, SpiderMonkey: 0.47, JavaScriptCore: 0.62, Nashorn: 1.8, ChakraCore: 0.025

(Yes, it appears that disabling :static-fns is really the right thing to do for this particular code if you are targeting ChakraCore!)

But, what if we now also enable :fn-invoke-direct? The code emitted for f in this case has a function body that looks like:

return (h.cljs$core$IFn$_invoke$arity$1 ? h.cljs$core$IFn$_invoke$arity$1(x) : h(x));

The only difference is that now, in the fallback branch, h is directly invoked via h(x), rather than using the more conservative h.call(null,x) construct.

Here are the speedups observed when both :static-fns and :fn-invoke-direct are enabled, compared to a baseline when :static-fns is disabled:

V8: 0.86, SpiderMonkey: 0.54, JavaScriptCore: 0.63, Nashorn: 2.2, ChakraCore: 0.025

In this case :fn-invoke-direct helped recover some of the performance drop. Another way to look at this improvement is the speedup where :static-fns is always enabled, comparing :fn-invoke-direct disabled vs. enabled:

V8: 2.1, SpiderMonkey: 1.1, JavaScriptCore: 1.0, Nashorn: 1.2, ChakraCore: 1.0

This last set of speedups is perhaps a more useful way of looking at things if you are going to assume :static-fns is enabled anyway, and want to see how :fn-invoke-direct might help things if it were enabled.

Given this, why isn't :fn-invoke-direct enabled by default when :static-fns is enabled? In other words, why is it a separate compiler option? There could still be corner cases where the more conservative invocation using call produces the correct behavior. For this reason, the option is disabled by default, and requires that you explicitly configure it in order that it be enabled.

I'd suggest giving :fn-invoke-direct a try on your codebase and see if it helps with performance!

Permalink

Software Engineer (Clojure)

Software Engineer (Clojure)

Status | Anywhere
remote
$60000 - $120000

Status

Status is a fast growing, completely decentralized startup developing powerful DApps (decentralized apps) for Ethereum that change the way we do things on the web. To do this we need passionate, talented & self motivated candidates to join our global decentralized team.

As a product, Status is an open source discovery tool for Ethereum that makes it easy for anyone to access DApps and services built on the decentralized web. Status also allows users to chat, make payments and more, securely using Ethereum's protocols.

Opportunity

It's still just the beginning, but the promise of Web 3.0 and Ethereum is here to stay. Our secret weapon is having passionate people on a mission to develop the gateway into this new world. If you want to be part of this adventure, join our disruptive distributed global team.

Job role

You will be part of our development team and will be responsible for shaping, developing and implementing our product, while ensuring that we are at the leading edge of technology. We are looking for a Clojure (Script) Developer.

Responsibilities

  • Deliver a high quality application from designing and iterating new features into production in short sprint cycles
  • Set up complex development, test and production environments
  • Independently design, build, maintain and take on ownership of complex software modules and services
  • Collaborate cross-functionally to inform about design and architectural decisions
  • Maintain and make use of a good overview of the overall technology stack
  • Work closely together with all engineering disciplines to ensure seamless user experience and efficient overall architecture
  • Shape, facilitate and survey coding guidelines and quality standards
  • Manage technical debt
  • Independently focus on continuous learning and improvement and drive the evolution of our engineering practices

Requirements

  • 5+ years experience building complex applications
  • Hands-on experience with Clojure (ClojureScript)
  • Experience with JavaScript and ReactNative is desired
  • Experience with Java and Objective-C is a plus
  • Development capabilities proven by great work results
  • Strong software engineering practices
  • You must be comfortable working with an existing codebase
  • Good communicative and interpersonal skills
  • Ability to pick up new technologies quickly
  • Fluency in written and spoken English
  • You are excited by P2P technologies and cryptocurrencies.

Working at Status

Status is a decentralized organization. You will not have a boss or a work schedule. What you will have is responsibility to your collaborators to do your best, keep up with your responsibilities, and support your team's priorities. At Status we are all working toward the goal of expanding and supporting the Ethereum space.

Permalink

Probabilistic programming example with Anglican

Probabilistic programming example with Anglican

At the last Amsterdam Clojure Meetup there was a great demostration by Zhenhao Li about Anglican. Anglican is a probabilistic programming language integrated with Clojure. I am a fan of the book Probabilistic Programming and Bayesian Methods for Hackers from which I solved a problem in Clojure, about which I wrote here:

Bayesian Inference with Markov Chain Monte Carlo in Clojure

Below is the solution to that problem using Anglican:

Read more

Permalink

The REPL

The REPL

More type worrying, cats and dogs, and cache lines
View this email in your browser

The REPL

-main

People are worried about Types. ?

  • There is a lengthy thread on the Clojure mailing list about spec's unintuitive (IMO) behaviour for validating keys. The specific part that I'm worried about is that no error is thrown at runtime if you specify a keyword in s/keys that hasn't been defined. It seems like the kind of thing that could leak into production, or mask bugs further down the line.
  • Semantic integrity checks is a new feature of a configuration language Dhall, which lets you verify that the semantics of your API haven't changed. I could imagine something similar being applied to APIs that have been specced in Clojure, to automatically detect breaking changes.

Foundations.

Tools.

Recent Developments.

  • Clojure 1.9.0 RC1 is out

Learning.

Misc.

  • Long time member of the Clojure[Script] community Chas Emerick has a Patreon up. If you've benefited from his work, either now or in the past, then consider sponsoring it.
Copyright © 2017 Daniel Compton, All rights reserved.


Want to change how you receive these emails?
You can update your preferences or unsubscribe from this list

Email Marketing Powered by MailChimp

Permalink

Scala Developer at LeadIQ (Full-time)

Are you the type of engineer who punches juke boxes to make the music start? Do you consider riding your motorcycle off into the a sunset a personal hobby? Is architecting a system from the ground up no big deal to you? We're looking for full-time Scala developer to make this happen.

The Product

We are on a mission to revolutionize Sales industry using data science. Our product helps our customers to collect and enrich their target prospects. Our internal data processing combines human intelligence and data science to enable our customers to find perfect contact information and save to their existing platforms like Salesforce, etc.

The Challenge

  • We are at an exciting stage in our growth. We are getting traction with big customers, scaling out, and solving increasingly complex engineering problems.

  • Our systems are mostly written in Scala. We have used Kafka as backbone to communicate between our API server and micro-services. Smart architecture design is crucial in order to guarantee our micro-services based systems run smoothly and reliably.

  • We're looking for someone who can drive our product backend integration features, refactor existing code for faster responses and becomes an important asset to the rest of the engineering team.

  • Data quality is one of the critical factors to make our product successful. We often have needs to process 3rd parties data and clean existing data using Spark. So you need to be comfortable writing Spark scripts.

  • We have very complex integrations with 3rd parties systems like Salesforce, etc. These integrations are core to what we're offering to our customers. We're looking for someone who is willing to listen to customer feedback to improve existing features and provide new features for customer success.

The Stack

Scala, Kafka, Spark, MongoDB, ElasticSearch, Docker, Vue.js

The Team

We want team members with attributes like:

  • Focus on delivering value to the customer
  • Strong belief in collaboration
  • Passion that drives you to execute and innovate
  • Ability to self-manage and take ownership of a feature
  • Ability to juggle many projects and responsibilities
  • Extremely entrepreneurial and self-driven
  • Not afraid of a steep learning curve
  • Passionate about building a big business that transforms the sales industry
  • Exceptional at writing scalable, production-ready code
  • Thrive in a fast-paced environment
  • Avoid over-engineering
  • Simple designs and fast execution
  • Discipline in following process and documenting your work

These personality traits define the culture we are building and are more important to us than a particular set of technical skills.

The Responsibilities

If you join LeadIQ, you will learn a lot: In terms of technical ability there are many cool tools, technologies, patterns and other great developers that will sharpen your skills. Personally you be given the chance to step up, lead and make your mark in a growing startup as we tackle the challenges in our next phase of growth.

On the technical front, we need you skilled in:

  • Scala (but experience in another functional language helps, e.g. Haskell or Clojure)
  • Play framework
  • Concurrency (futures, actors, basic understanding of threads)

So if you feel like you're a good fit for us, drop us a line! We love meeting developers who are excited by our product!

Get information on how to apply for this position.

Permalink

PurelyFunctional.tv Newsletter 251: Machine Learning, ClojureScript, The Future

Issue 251 – November 13, 2017

Hi Clojurers,

I surprised myself last week by signing up Kim Crayton to speak at Clojure SYNC! She’ll be helping us stay relevant as AI takes our jobs.

Please enjoy the issue.

Rock on!
Eric Normand <eric@purelyfunctional.tv>

PS Want to get this in your email? Subscribe!


David Nolen on ClojureScript Podcast

Joy Clark interviews David Nolen mostly about ClojureScript on the Conversations about Software Engineering poscast. Some great stuff. Check out her sketchnotes.


WFT?: What’s the Future and Why It’s Up to Us Book

This book by Tim O’Reilly explores what the future holds technologically and economically. It has an optimistic perspective but urges us to shift our mindsets because the future is going to happen regardless!


Designing the Future through Sci-Fi World Building Podcast

Monika Bielskyte was interviewed on the Voices of VR podcast (Kent Bye) about how Hollywood Sci-Fi is failing us.


Cats and Dogs in Cortex Redux

A machine learning walkthrough by Carin Meier.


Software 2.0

Andrej Karpathy argues that Deep Learning Neural Networks is a new paradigm for building software. I’m not so sure, but it’s a new perspective.


State in Re-frame

I’ve put together a new guide to Re-frame. This time, it’s all about where to put your state.


Avoid Converting JavaScript Objects

Mike Fikes has a good explanation of what to do instead of converting all of your JS objects into Clojure maps. He shows that doing so is 10 times slower that accessing the values using the Google Closure goog.object/getValueByKeys. I never remember to do this, but I’ll be on the lookout now.


Currently recording: Understanding Re-frame Course

There are now almost 3 hours in this course, and I’ve only gotten started with my notes. This one is going to be a monster! Re-frame deserves a lot of time because I think it’s the best frontend framework we’ve got going.

Here are new lessons since last week:

  • Lists, keys, and laziness in Hiccup
  • Accessing DOM nodes
  • Building form components
  • Reagent Atoms
  • Reasoning about Re-rendering

Look for an Early Access Program sale in the coming week.


New Feature: Course progress

I’ve known for a while that my alphabetical list of courses is less than ideal for figuring out what course you should buy or watch next. I’ve got filtering, but I wanted a way for my members to track their progress in individual courses. Well, I’ve built a new feature. You can mark individual lessons as completed by clicking a checkmark. And the course will show you a progress bar. Look for integration with filtering to come soon. I’d love to know what you think or if you have ideas that will help you navigate the 57 hours of video I have on the site.

Permalink

Copyright © 2009, Planet Clojure. No rights reserved.
Planet Clojure is maintained by Baishamapayan Ghose.
Clojure and the Clojure logo are Copyright © 2008-2009, Rich Hickey.
Theme by Brajeshwar.