Genericity, Serendipity, Surety

| 4 Comments

I read everything Aristotle Pagaltzis writes carefully. I often agree. You should too.

Aristotle responded to my practical/philosophical tirade against manual type checks in Perl 5 APIs with a comment that bears consideration:

Many objects are blessed hashes. However, most of the time, that is an implementation detail rather than an advertised part of the API... the question you should ask is not "can I treat this like a hash?" but "am I supposed to treat this like a hash?".

He's right. That's a potential design flaw in my suggestion to attempt to treat what should be a hash reference as a hash reference. Of course, the opposite design flaw is to disallow perfectly valid values that should work just fine if you treat them as hash references.

You get to pick which theoretical axe to grind there.

(Aristotle also recommends the use of UNIVERSAL::ref to allow objects and classes to provide their own ref() methods to return appropriate values. That's a good solution if you're working with code that uses the ref() technique to check for type appropriateness. If you're working with library code which uses a mishmash of all of the various techniques which catch some cases but not others... that's where the simplicity of eval { ... } shines for me. Do consider UNIVERSAL::ref, however. Along the same lines, Burak Gursoy noted that Scalar::Util::Reftype has a better API than reftype() from Scalar::Util.)

Your theoretical bias determines the way you write your APIs, even if you don't realize you have a theoretical bias.

If you take my approach, you give the programmer the responsibility of not passing in objects which shouldn't be dereferenced as hashes. If you pass in a blessed hash reference, my code assumes you intended it to work like a hash reference. Yes, you could pass in an object accidentally, but I prefer to allow people to do clever things (like passing in an overloaded object) when necessary, rather than forbidding them.

If you take the other approach, you give the programmer more safety against accidentally passing in the wrong thing unintentially while removing some possibility for cleverness that may be necessary in some cases. Please note: I'm not saying that this is what Aristotle himself prefers; I merely used his comment to illustrate this possibility.

My approach is not always right in every circumstance. I don't always use it in every circumstance. Yet I use it as a design principle: I don't want to forbid intelligent people from doing clever things I didn't intend because I didn't imagine a concrete use for them when I designed the API. Quite the opposite! I want people to use APIs I write to do things I couldn't imagine. That, to me, is a sign of success.

Some people will abuse them. Some people will misuse them. Good documentation and great examples help. (Modern Perl schadenfreude means shaking your head in disbelief when you see the dreadful my $io = new IO::Socket::INET->new( ... ) idiom repeated in 2009.)

Yet my experience writing copious tests for and maintaning plenty of Perl 5 code suggests that treating types checks as "What can you do?" not "How do you do it?" or "What are you?" improves genericity, improves reusability, and expands the possibilities for happy serendipities. I can't prevent inexperienced or bad or malicious coders from doing bad things with my APIs, but I can allow disciplined and smart and well-cultured programmers to do great things with them by not getting in their way.

4 Comments

Can you link to the article or argument why my $io = new IO::Socket::INET->new( ... ) is dreadful and what the alternative is? I just don't get your reference at all.

Easy explanation: why call new() twice?

Did not see that first new. Sorry.

There are three parties in play here, the callee, the caller, and the object. It’s true that using ref makes it very hard for the caller to override the wishes of the object. It would be nice if there was an easy way for the caller to be explicit.

I don’t want to forbid clever things. I just don’t like code that is unsafe by default in the name of allowing clever things. What really sucks is when you’re forced to choose between the two, which Perl 5 does in a number of cases – and this is one of them.

What’s worse is that sometimes, it leaves things ambiguous. I have been working on a sugary interface where some methods take arbitrary mixed lists of strings and hashes. The problem is that this is really a list of stringifiables and hashlikes, and when I am looking at an object, I have no good way to tell a) which one the object wants me to see it as, much less b) what the caller wants me to do with it. After much mental churn I am settling for a simple 'HASH' eq ref $foo as the most optimal thing I can do.

Modern Perl: The Book

cover image for Modern Perl: the book

The best Perl Programmers read Modern Perl: The Book.

sponsored by the How to Make a Smoothie guide

Categories

Pages

About this Entry

This page contains a single entry by chromatic published on January 8, 2010 3:36 PM.

Perl Type Checks versus Encapsulation was the previous entry in this blog.

Optional Types and Perl is the next entry in this blog.

Find recent content on the main index or look in the archives to find all content.


Powered by the Perl programming language

what is programming?