"The trick wasn't really so much the coding but coming up with how it organizes the data."
I think this really is the key to understanding git as well. When you understand the git data structures, git makes sense. Otherwise, it can be quite difficult to grasp (http://ftp.newartisans.com/pub/git.from.bottom.up.pdf was really useful for me).
Not only git. There is a nice quote from Fred Brooks Jr. (I think), that if you show me your flow charts and algorithms, I remain as clueless as before, but show me your data structures, and everything else will follow. (I am paraphrasing this from memory, Brooks put it more eloquently.)
Yes! I love that quote - so true. Linus has the same opinion:
"Bad programmers worry about the code. Good programmers worry about data structures and their relationships." [1]
I actually refered to this when discussing switching from Java to Python. One of the biggest problems for me was not easily being able to see the types of the arguments to a function in Python. [2]
> not easily being able to see the types of the arguments to a function in Python. [2]
That's because of Duck Typing [1]. I really like Python for how quick and easy it is to make small pieces of software, but I fully agree that Duck Typing can become more of a hindrance on larger projects.
Nothing about duck typing requires a python-like type system. F# has duck typing via inlining (hacky sorta), C# has it too, but only for compiler use, not end user code (because why build generic features, when you can hack one-offs into the compiler).
It'd be great to have more static duck typing. I suppose that's the point of traits and mixins, to some extent. But it'd be nice to have on-the-fly constraints created and enforced.
I thought that F# used type-inferencing? If not, then its not nearly as similar to Haskell and OCaml as I've been led to believe.
Unless you're referring to type-inferencing when you use the term static duck-typing. The term duck-typing was never meant to refer to type-inferencing - duck-typing is type-opacity right into the runtime. The whole point of duck-typing is that objects can be referred to generically even in cases where their underlying type would be undecidable.
Type-inferencing gives you some of the source-level flexibility of not having to refer directly to an object's type much like duck-typing, but without many of the crazy runtime disasters that you can get with duck-typing.
Parent comment is referencing the "let inline" construction that in addition to inline code allows to constrain generic types structurally over member definition[1] instead of by name as usual. This gives us poor's man type classes which is useful nonetheless.
I guess the conclusion is that the definition of duck typing is fuzzy and misleading.
Off topic from the article, but this is something I literally just spent my morning wrestling with and fixing, so I'd like to talk/rant about it a little. My apologies in advance for rambling.
You can use doc comments to signify types. You mention you use PyCharm in your article, and it supports doc comments in it's autocomplete and code analysis, I find most popular libraries are commented well enough that PyCharm can understand them.
I also always try (and encourage others) to use names that are declarative for both purpose and type. Of course, you can't always rely on third party libraries (or even colleagues) to be so nice, but I find a good name (almost) always removes all the confusion that normally comes from a lack of static typing.
For example, one of Codecademy's very first Python lessons includes some code like this:
meal = 44.50
tax = 0.0675
tip = 0.15
Which I think is unclear. Imagine these were function arguments. calc_total(meal, tax, tip) is vague. Is meal an object, or a numeric value? (I could possibly see it containing a list of all items in the meal with prices). Tax is almost always a percentage, but what about tip? Judging by the the above values, we can assume a 15% tip, but we don't know if the customer was stingy and tipped $0.15, and by just name alone, we can't tell at all.
calc_total(meal_cost, tax_percent, tip_percent)
It's now immediately clear to me the type and range of values it accepts. The tip_percent is also an example of when a good name can provide info that even static typing could not, because in either case it is a floating point (please let's not get into a debate about Decimal or currency types :P ). This is a very basic example, but it applies at all levels. Don't call the parameter "users" if the function is not expecting an iterable of User objects. Maybe "usernames" would be better. Etc.
But of course, this only helps if the code you're working with is named well. In something like Java, you have better protection and tools when working alongside lower quality code. I also completely agree with you on point #2 about No Static Types. Navigating through my editor is so much easier in a static language than it is in PyCharm with large projects. And the most annoying thing is that autocomplete breaks with ORMs, and most ORM usage is actually flagged as a warning or error. Ugh.
I also feel very confident in the automated refactoring in something such as an IntelliJ Java project, or ReSharper, but am apprehensive about using PyCharm to refactor anything with usages spanning more than one file. Same goes for Javascript (or any other dynamic language, I suppose. Those are just the two I use).
Enjoyed the posts by the way, adding your blog to my reading list.
So even with the types, you have exactly the same problem as in python, and creating a special class to wrap up percentages is something I find quite heavy.
Your alternatives would be to have naming conventions, like you do, or some proper documentation that tells you what the function expects for its arguments, with an example of a function call, which I particularly like in python because of the REPL.
This one makes sense in the real world, so I suppose an advanced type system allows the programmer to specify what operations are legit or not across different types.
One could imagine systems for that, but units of measure doesn't do any customization of different operations. It's just unit checking for for arithmetic, like in high-school math.
> So even with the types, you have exactly the same problem as in python, and creating a special class to wrap up percentages is something I find quite heavy.
Yep, I agree completely and was hoping to make that point. The typing makes it clear that meal is going to be a single price, and that tip won't be a String or something unexpected, but it is still unclear whether it is a percentage or a amount.
I guess what I mean to say is that while static typing certainly helps, it isn't the be-all and end-all of code clarity, and descriptive names can really help code readability regardless of your typing system.
Also, admittedly the example I provided is rather simple. And in any language, you should be using a proper Currency type or library when dealing with money, which would make most of my argument moot:
I think this really is the key to understanding git as well. When you understand the git data structures, git makes sense. Otherwise, it can be quite difficult to grasp (http://ftp.newartisans.com/pub/git.from.bottom.up.pdf was really useful for me).