Coming from an OO-background I instinctively turned to protocols during my early Clojure explorations. Need to model an Aggregate? Complex internal structure, should be hidden, small set of functions operating on it? Protocol, what else?
Eventually I've learnt a bit of FP and started moving towards pure functions, feeling less and less the need to resort to the OO-ish features of the language. However recently I've been spending some time writing a Twitter-reader application and had some trouble with functions with side-effects. Long story short, I've come up with a simple rule of thumb (don't expect anything earth-shattering)
Use protocols when state is involved!
When is 'state' involved?
1. when the program maintains stateful entitites (either in DB or just in memory)
2. communicating with an external system (e.g. Twitter)
The two cases are not that different, actually. If you can abstract away from the communication details, is operating on the DB really much different from operating on Twitter? Twitter could run on your local machine on your DB after all, in theory.
So what's the benefit?
1. you can group functions together. Usually if you manage state, there are multiple operations around it. Save/Delete/Get/Update for DB, StartListeningToStream/StopListeningToStream for Twitter. Having separate methods only grouped by namespace seems somewhat off to me. Maybe a matter of taste.
2. Easier to provide new implementations. You want to replace your Mongo with Reddis, just give a new implementation. I think the cognitive load is much less if all the functions you need to change are bound together by the language itself, so you don't have to hold all those independent functions in your head.
3. Ordinary functions should be pure. If you hide your state behind a protocol, all the rest can be
An example
So the new, all-encompassing, completely universal principle you should absolutely start all your projects with, absolutely without exceptions
1. identify the moving parts in your model
2. hide them behind protocols by having a namespace for each where only the protocol itself and the factory method is public
Still a bit OO-ish? Maybe. But no-one said OO is without merits.
Thursday, 7 May 2015
Saturday, 18 April 2015
Creating Clojure "actors" with core.async
A plain vanilla "actor" built with core.async. Can wrap any type of logic as long as it can be described with a function with the given - very general - signature.
Labels:
Clojure
,
core.async
Thursday, 16 April 2015
Improving existing code - imaginary checklist - 3
Eliminate cyclic dependencies
Almost forgot about this one, despite being the most obvious (and challenging, possibly).
Decrease ACD (Acyclic Component Dependency)
Related to the previous point, but a quite vague target. I don't know what the proper value is for relative ACD, it possibly depends on the size of the application and other traits of it. STAN is a great tool to help you in that, however.
Almost forgot about this one, despite being the most obvious (and challenging, possibly).
Decrease ACD (Acyclic Component Dependency)
Related to the previous point, but a quite vague target. I don't know what the proper value is for relative ACD, it possibly depends on the size of the application and other traits of it. STAN is a great tool to help you in that, however.
Tuesday, 31 March 2015
Improving existing code - imaginary checklist - 2
Continuing the completely random and unprioritized checklist. Some of them are easy to fix, some are heavy.
Eliminate mutable state where possible
Immutability has many advantages over mutable objects, none of them will I mention here. For an example see the Query class in the previous post. Avoid setters, use final keyword and defensive copies.
Thread safety
Quite self-explanatory.
Separation of concurrency and domain logic
Multi-threading logic should be separated from the rest of the code. It's quite complex on its own, even more when is intertwined with other things.
Invariants and UnitOfWork-s
Check what are the units of work, and what are the invariants. The formers should encapsulate the latter. This is quite an important point. And a not so low hanging fruit, probably.
Push third-party code to the outer layers
This might be a big one. Practically I think almost all application should follow the Hexagonal Architecture style. This can be achieved iteratively, but could take a long time to get there if the code hasn't been written that way.
To be continued...
Eliminate mutable state where possible
Immutability has many advantages over mutable objects, none of them will I mention here. For an example see the Query class in the previous post. Avoid setters, use final keyword and defensive copies.
Thread safety
Quite self-explanatory.
Separation of concurrency and domain logic
Multi-threading logic should be separated from the rest of the code. It's quite complex on its own, even more when is intertwined with other things.
Invariants and UnitOfWork-s
Check what are the units of work, and what are the invariants. The formers should encapsulate the latter. This is quite an important point. And a not so low hanging fruit, probably.
Push third-party code to the outer layers
This might be a big one. Practically I think almost all application should follow the Hexagonal Architecture style. This can be achieved iteratively, but could take a long time to get there if the code hasn't been written that way.
To be continued...
Binary Tree implementation in Haskell, Clojure and Scala - 2
And thanks to @Kaloz, here is the Scala implementation with a slightly altered/extended functionality.
Improving existing code - imaginary checklist - 1
I endeavour to prepare a checklist I would go through if I had to start to work on a brownfield project. Small things to improve, hunt for the low hanging fruits. Don't expect any enlightenment here, these are just pearls of blue-collar wisdom.
Handling unrecoverable exceptions
In my current project we use messaging via ActiveMQ. Should an exception occur, the message is bounced back to the queue, then retried. It's fine, as long as there is a chance of recovery and sometimes - for example when the message is invalid in some way - there is none. In this case bouncing the message back is a waste of time and resource, plus the message can and up in the Dead Letter Queue leading to memory loss. So instead we should catch these exceptions as close to the entry point as possible and simply log them.
Validating input
Related to the previous point. To adhere the fail-fast principle, the inputs of the system should be validated. Validation is usually against some domain criteria, so I would put the logic in the Domain layer, just as the input passed the ACL. Should the input fail to comply, throw an unrecoverable exception.
Validate domain objects
I wouldn't stop at the inputs. I'd validate every domain object upon creation. Design by contract is a very good practice.
The validation should throw an unrecoverable exception. See the first point.
To be continued...
Handling unrecoverable exceptions
In my current project we use messaging via ActiveMQ. Should an exception occur, the message is bounced back to the queue, then retried. It's fine, as long as there is a chance of recovery and sometimes - for example when the message is invalid in some way - there is none. In this case bouncing the message back is a waste of time and resource, plus the message can and up in the Dead Letter Queue leading to memory loss. So instead we should catch these exceptions as close to the entry point as possible and simply log them.
Validating input
Related to the previous point. To adhere the fail-fast principle, the inputs of the system should be validated. Validation is usually against some domain criteria, so I would put the logic in the Domain layer, just as the input passed the ACL. Should the input fail to comply, throw an unrecoverable exception.
Validate domain objects
I wouldn't stop at the inputs. I'd validate every domain object upon creation. Design by contract is a very good practice.
The validation should throw an unrecoverable exception. See the first point.
To be continued...
Tuesday, 24 March 2015
Binary Tree implementation in Haskell, Clojure and Scala
I've embarked on learning Haskell recently. I only start to suspect the power of this language, but I'm already impressed with its beautifully succinct and readable, no-nonsense syntax. Nuff' said, let's do a demonstration!
Haskell
Clojure
Scala - I've failed with this one. Maybe @Kaloz can help me out.
Haskell
Clojure
Scala - I've failed with this one. Maybe @Kaloz can help me out.
Subscribe to:
Posts
(
Atom
)