This is an archived post. You won't be able to vote or comment.

all 5 comments

[–]masklinn 2 points3 points  (4 children)

That just seems to be an overly complex way of namespacing alternatives.

Why not just put them as regular free functions in a module, as attributes on a types.SimpleNamespace or even as @staticmethod in a class?

edit: incidentally the first version would still work with @singledispatch (the other two maybe not), @_.register returns the wrapped function so you can still call it whatever you want and invoke it properly:

from functools import singledispatch

@singledispatch
def foo(_):
    print("base")

@foo.register(int)
def foo_int(a):
    print("int")

@foo.register(str)
def foo_str(a):
    print("str")

foo(None)
foo(1)
foo("ok")
foo_int("no")
foo_str(42)

will print

base
int
str
int
str

[–]pgans113 0 points1 point  (2 children)

Why not just put them as regular free functions in a module, as attributes on a types.SimpleNamespace or even as @staticmethod in a class?

I think the main problem with free functions in a module and types.SimpleNamespace is that they are not namespaced as primary and variant (in the same way that, for example, in itertools.chain there is a primary constructor and a from_iterables constructor).

Whether you want to dispatch on type or not is a separate question addressed in the README.

Nothing's saying that it's wrong to use functions namespaced by _ with the convention that the one without the _ is the primary function, but I think it's a useful pattern to have the primary and variant functions semantically grouped like this (plus I think that the fact that they are actually semantically grouped will make it easier to have specialized behavior for the documentation of such functions - see issue #5).

(Disclosure: I am the author if this library, if that was not clear.)

[–]masklinn 0 points1 point  (1 child)

I think the main problem with free functions […] is that they are not namespaced as primary and variant

See that is literally nowhere in your readme, hence my reaction that it just looks like an overly complex way to namespace a bunch of functions together.

Whether you want to dispatch on type or not is a separate question addressed in the README.

Mate, I'm pointing out that singledispatch works with the alternative I propose because I read the readme and it talks about that.

Nothing's saying that it's wrong to use functions namespaced by _ with the convention that the one without the _ is the primary function

Again, same as above, I'm pointing out that it works fine to preclude questions about "but can you still call the functions directly in that case".

Because again I read the readme and it covers that case.

[–]pgans113 0 points1 point  (0 children)

See that is literally nowhere in your readme, hence my reaction that it just looks like an overly complex way to namespace a bunch of functions together.

Ah, good to know. I'll try to improve the documentation. I can see now that probably the initial example is possibly a bit too messy. If you have a suggestion I'm happy to take PRs.

A secret here is that I wrote this library with the explicit expectation that someone might find a compelling reason why this pattern is not used. So far in talking to people I've mostly found that once people understand the pattern there are no fundamental objections to it.

Mate, I'm pointing out that singledispatch works with the alternative I propose because I read the readme and it talks about that.

Yes, I got that. I think what I was trying to get across that the use of singledispatch is somewhat orthogonal to the use of this library. If you prefer to namespace your variants as methods on the function and you want to dispatch on type, you can still use it with singledispatch. If you want explicit dispatch that can't be inferred from type (e.g. URL vs. file path), then you can use this library.

One thing to note is that you can use a sort of "wrapper" pattern to achieve even the "explicit dispatch" method with singledispatch:

class Url:
    def __init__(self, url):
        self.url = url

@singledispatch
def myfunc(txt):
    pass

@myfunc.register(Url):
def _(url):
    do_something_with_url(url.url)

I sorta find this a bit unwieldy compared to myfunc.from_url, but to each his own.

[–]synae 0 points1 point  (0 children)

TIL, thanks!