Returning different types based on input parameters

George Sakkis george.sakkis at gmail.com
Wed Apr 8 10:09:30 EDT 2009


On Apr 7, 3:18 pm, Adam Olsen <rha... at gmail.com> wrote:

> On Apr 6, 3:02 pm, George Sakkis <george.sak... at gmail.com> wrote:
>
> > For example, it is common for a function f(x) to expect x to be simply
> > iterable, without caring of its exact type. Is it ok though for f to
> > return a list for some types/values of x, a tuple for others and a
> > generator for everything else (assuming it's documented), or it should
> > always return the most general (iterator in this example) ?
>
> For list/tuple/iterable the correlation with the argument's type is
> purely superficial, *because* they're so compatible.  Why should only
> tuples and lists get special behaviour?  Why shouldn't every other
> argument type return a list as well?

That's easy; because the result might be infinite. In which case you
may ask "why shouldn't every argument type return an iterator then",
and the reason is usually performance; if you already need to store
the whole result sequence (e.g. sorted()), why return just an iterator
to it and force the client to copy it to another list if he needs
anything more than iterating once over it ?

> A counter example is python 3.0's str/bytes functions.  They're
> mutually incompatible and there's no default.

As already mentioned, another example is filter() that tries to match
the input sequence type and falls back to list if it fails.

> > To take it further, what if f wants to return different types,
> > differing even in a duck-type sense? That's easier to illustrate in a
> > API-extension scenario. Say that there is an existing function `solve
> > (x)` that returns `Result` instances.  Later someone wants to extend f
> > by allowing an extra optional parameter `foo`, making the signature
> > `solve(x, foo=None)`. As long as the return value remains backward
> > compatible, everything's fine. However, what if in the extended case,
> > solve() has to return some *additional* information apart from
> > `Result`, say the confidence that the result is correct ? In short,
> > the extended API would be:
>
> >     def solve(x, foo=None):
> >         '''
> >         @rtype: `Result` if foo is None; (`Result`, confidence)
> > otherwise.
> >         '''
>
> > Strictly speaking, the extension is backwards compatible; previous
> > code that used `solve(x)` will still get back `Result`s. The problem
> > is that in new code you can't tell what `solve(x,y)` returns unless
> > you know something about `y`. My question is, is this totally
> > unacceptable and should better be replaced by a new function `solve2
> > (x, foo=None)` that always returns (`Result`, confidence) tuples, or
> > it might be a justifiable cost ? Any other API extension approaches
> > that are applicable to such situations ?
>
> At a minimum it's highly undesirable.  You lose a lot of readability/
> maintainability.  solve2/solve_ex is a little ugly, but that's less
> overall, so it's the better option.

That's my feeling too, at least in a dynamic language. For a static
language that allows overloading, that should be a smaller (or perhaps
no) issue.

George



More information about the Python-list mailing list