/Too many arguments at work are over dogma and I have to spend a lot of my time reminding people that there are options. That's all. There are options. Your kneejerk response to a problem might be fine -- might be right -- but there are always options.
That's when I understood where TDD came from: dynamically typed languages require so many tests to work reliably that we better write those tests first so we're not tempted to omit them.
Anyway, I redid the whole thing in OCaml, and this time got lots of compile time errors. Which I could correct, and once the compiler was happy, well… my program was basically correct.
---
I still don't rule out that other people's brains are wired fundamentally differently. I don't expect it, it would surprise me, but I honestly don't know. What I do know is that dynamic typing is not for me. I'll suffer through it to get other advantages (Python's comprehensive library for instance), but that's about it. Dynamic typing and I are otherwise done.
The key to writing type-less code is making it so simple that you could cry. Avoid being clever at all costs. However, it's almost the exact opposite when you have a nice compiler checking everything, you can be mighty clever and know that it will work.
I'm lucky in that we can solve a problem in a myriad of languages, depending on if it needs to be "realtime", or can be precomputed. There are some types of problems I'd rather solve in Scala, and others, Python or PHP. It just depends.
I can't help but interpret this as "the key to writing type-less code is choosing problems so simple that you could cry". In other words, dynamic typing doesn't scale.
When I have a very simple problem to solve, sure, I won't mind dynamic typing. For anything worth more than an hour of coding however, I'll definitely reach for more reliable tools.
If you write code for a type that "has" stringiness, as expressed by a trait, then anyone can build a variant of their types that expresses that trait.
One other thing that I DO like about type-less, is the ability to introspect, for example, in PHP:
``` foreach ((array) $my_class as $property => $value): ```
vs. a bunch of boilerplate and making sure types match up in most strongly-typed languages.
Things like that are actually possible to do in a light weight way in FP languages and many of them are part of the stdlib. (PureScript happens to be my favorite example.) Many times it brings back the same feelings I had working in Ruby (which I also happen to love), but with the added confidence of a strong type system.
IMHO, type-less provides some resiliency. If you write code for a "string" anything that satisfies "stringiness" will still run just fine. This lets you build types that "trick" the code you're calling, which can be useful sometimes. I think I've abused this capability less than 3 times, but more than once. Mostly it allowed me to significantly change behavior without rewriting huge chunks of core code. However, these were all proof of concepts. Don't get it twisted, working with no types is pretty annoying sometimes.
Types, on the other hand, are great for most things, but I do find they get in the way sometimes. It's annoying when you look at the code you're calling and see that it only uses `.x` on a type you pass it, but you have to completely build a type just to create a `.x` on the type you pass in. I once found a NullPointer bug in HDFS with a static type (very strange) and had to work around it doing exactly this. Anyway, I think I'd like Go's approach to interfaces.