I'm still torn between dynamic and static typing.
The main disadvantage of dynamic typing for me is the lack of verifiable documentation about what data a function needs, and which data comes out of it. This becomes a problem when
a) the data is complex (e.g. dictionary of lists of items with certain properties)
b) I haven't looked at the function for a while.
In those cases, I find statically typed code easier to reason about.
On the other hand, it absolutely maddens me that static type systems force me to spell everything out, even where it's trivial. It slows me down, it bloats the code (especially when you have to create a new class for everything) - which again makes it harder to see the purpose of the code instantly.
That's why I decided to make my own little pragmatic experiment:
In cases where complexity is expected (or experienced) - and only there- I use the Clojure pre- and post-conditions to check the shape of selected parameters and/or its return value.
It looks like this:
(defn german-holidays
"Returns map of german holidays, in the year of d."
[d]
{:post [(like {(date 1 1 2012) :easter} %)]}
...)
(defn calendar
[c start-date end-date]
{:pre [(like {:appointments {} :new (list)} c)]
:post [(like {(today) {:occupations [] :appointments
#{}}} %)]}
...)
You can instantly see how the data is supposed to look like, it will be checked automatically, and it is close to the actual code where you need it (unlike e.g. Unit Tests).
I'm not yet sure how this experiment will fare in the future, so any suggestions or warnings are appreciated.