``Potentially-idempotent adapter functions are a honking great idea - let's do more of those'', to paraphrase the timbot.
-- Alex Martelli, on comp.lang.python
All programs that use GOTO's can be rewritten without GOTOs, using higher-level
constructs for control flow like function calls, while
loops, and so on.
In the same way, type checks - and even interface checks - are not essential
in the presence of higher-level control constructs such as adaptation. Just as
getting rid of GOTO ``spaghetti code'' helped make programs easier to read and
understand, so too can replacing introspection with adaptation.
In section 1.1.3, we listed three common uses for using type or interface checks (e.g. using isinstance()):
By now, you've seen enough uses of the protocols module that it should be apparent that all three of the above use cases can - in principle - be handled by adaptation. However, in the course of moving PEAK from using introspection to adaptation, I ran into some use cases that at first seemed very difficult to ``adapt''. However, once I understood how to handle them, I realized that there was a straightforward approach to refactoring any introspection use cases I encountered. Although this approach seems to have more than one step, in reality they are all variations on the same theme: expose the hidden interface, then adapt to it. Here's how you do it:
Notice that in each case, the code is demonstrably improved. First, there is more documentation of the intended behavior (as opposed to merely the actual behavior, which might be broken). Second, there is greater extensibility, because it isn't necessary to change the code to add more type cases. Third, the code is more readable, because the code's purpose is highlighted, not all the possible variations of its implementation. In the words of Tim Peters, ``Explicit is better than implicit. Simple is better than complex. Sparse is better than dense. Readability counts.''
Now that we've covered how to replace all forms of introspection with
adaptation, I'll readily admit that I still write code that does introspection
when I'm in ``first draft'' mode! Brevity is the soul of prototyping, and I
don't mind banging out a few quick if isinstance():
checks in order to
figure out what it is I want the code to do. But then, I refactor, because
I want my code to be... adaptable! Chances are good, that you will too.