From there I just started small by building some command line applications to solve every day needs. Many enjoy solving problems at http://www.4clojure.com as a method of learning.
Perhaps one of the main differences for someone coming from an OO world is that in OO, to learn an SDK means learning about all the classes and the methods they expose to operate on encapsulated data. You're limited to what you can do on the data via those public methods.
In a functional language, you instead learn a large amount of functions that operate on raw data directly. Then you build your app by creating functions that take an input and transforming it into a new output using these common set of functions.
One of the exciting things for me is Clojure Spec. One of the recurring problems in web development in an OO app is that you have some domain class like User and you have some service function which does CRUD on that User.
// Look how great our model layer is!
createUser(User)
Then as the app grows you have another service function which takes a Order object and 80% of the properties of User.So you may start with this:
// uses 100% of order, 80% of user
doSomething(Order order, User user)
Then you have another service function which takes 3 object but only uses a few properties of each: // this feels like a code smell
doAnotherThing(Order order, User user, SomethineElse somethingElse)
If you have a large team without a strong common practice an application can quickly turn into a ball of spaghetti: // let's list out specifically what this function takes
doAnotherThing2(long orderId, double orderTotal, long userId, String userName, A something1, B something2)
And then you reach some maximum amount of parameters and refactor it to this: doAnotherThing3(DoAnotherThingRequest request)
DoAnotherThingRequest feels right but it's redefining the types of all of the properties already defined in our domain model: class DoAnotherThingRequest {
long orderId;
double orderTotal; ... }
And then you have another function which needs DoAnotherThingRequest and a few properties of another class: class YetAnotherThingRequest extends DoAnotherThingRequest {
A something; ... }
And eventually you realize this is stupid and instead you do this: // Let's just use the raw JSON!
doAnotherThing4(JSON json)
Clojure Spec on the other hand makes much more sense to me. You can define specs on namespaced keywords: :order/total is an integer
:user/userName is a string with a maximum length of 10
And then your function can specify the shape of the data they receive: ; this requires an order total and a userName (Note: it doesn't redefine their specifications)
(s/def ::do-another-thing-request (s/keys :req [:order/total :user/userName ...])))
This makes much more sense to me as a way of building large systems. And then you find out that Clojure spec can generate tests on your functions automatically based on the specifications you created and your mind is blown. :)