On returning consistent data types

This post is ins­pi­red on an is­sue I on­ce foun­d, whi­le I was using a we­ll-k­no­wn li­bra­ry in Py­thon fo­r ­par­sing YA­ML fi­le­s. The pro­blem was that when it was loading the con­tent of the fi­le, the re­sult wa­s ­not co­he­ren­t, be­cau­se so­me­ti­mes it re­tur­ned the con­tent as a py­thon dict, but if the fi­le was emp­ty, the ­re­turn va­lue was No­ne.

Do you no­ti­ce so­me­thing odd he­re?

What if I want to use the re­sul­t? I can­not do it safe­l­y, for exam­ple:

1
2
3
content = yaml.load(...)  # with the correct parameters and file name
for tag, values in content.items():
    pass  # process as required...

If content is None, it will raise an AttributeError saying that None has no attribute called “items” (which is true).

The­re­fo­re, the de­ve­lo­per should ca­tch the ex­cep­tion or avoid the cor­ner ca­se, by doing so­me­thing like the fo­llo­win­g:

content = yaml.load() or {}

That could be a case of “coding defensively”, making sure that the program will not fail under most conditions (it would also require to add an assert or to raise an exception perhaps, but that is a different topic). I actually agree with defensive programming, but I think it is better if the library itself has a more correct behaviour, respecting the interface (that is: if you are going to return a dictionary, and there is not content, then the logical assumption is to expect an empty dictionary). This must be the default behaviour, not something to be set by parameters.

This could be thou­ght as an ins­tan­ce of a mo­re ge­ne­ral pro­blem that oc­curs when so­me func­tio­n is in­ten­ded to re­turn “X or Y”. In my opi­nio­n, if X and Y do not sha­re the sa­me in­ter­fa­ce, ­the­re is a po­ten­tial bug (in the Ob­jec­t-O­rien­ted pa­ra­digm we would say that the­re is no po­l­y­mor­phis­m, or ma­y­be that the “con­trac­t” is not being res­pec­te­d).

This is an exam­ple that I wanted to hi­gh­li­gh­t, be­cau­se it mi­ght help you to wri­te clea­ner co­de.