Hacker News

36 Comments:
anentropic said 4 days ago:

This is pretty horrible.

And you could achieve the same thing with built-in functools.singledispatch by designing your API better

e.g. the example given is:

    int area(int length, int breadth) {
      return length * breadth;
    }

    float area(int radius) {
      return 3.14 * radius * radius;
    }
What if you need to calculate the area of another shape that uses a single integer value as the parameter?

With singledispatch and some types:

    import math
    from functools import singledispatch
    from typing import NamedTuple

    class Rectangle(NamedTuple):
        length: int
        breadth: int

    class Circle(NamedTuple):
        radius: int

    @singledispatch
    def area(val) -> None:
        raise TypeError(val)

    @area.register
    def _(rectangle: Rectangle) -> int:
        return rectangle.length * rectangle.breadth
    
    @area.register
    def _(circle: Circle) -> float:
        return math.pi * math.pow(circle.radius, 2)
BiteCode_dev said 4 days ago:

Again this is over-engineering. Put functions in modules to namespace them and be done with it.

If you really want types, use classes. It removes the need for single dispatch, creates a self documenting namespace and saves you an import when you want to do the calculation.

    import math
    from dataclasses import dataclass

    @dataclass
    class Rectangle:
        length: int
        breadth: int
        def area(self) -> int:
            return self.length * self.breadth

    @dataclass
    class Circle:
        radius: int
        def area(self) -> float:
            return math.pi * self.radius ** 2

@dataclass is of course not mandatory, and just make the code shorter.

We are talking about very simple things here. No need to add complexity to them.

anentropic said 4 days ago:

Yes I agree, if I was writing this for my own project I'm pretty sure I would do what you have done and use classes with an `area` method. (FWIW I think the dataclass approach is nicer for this example than namespacing separate `area` functions using modules)

You could define it as a typing.Protocol too.

My point was just that, even if for some reason you prefer function overloading, the restrictions needed to fit within the constraints of functool.singledispatch arguably lead to cleaner code than the complicated mess in the OP article.

This would look messy if you implemented as just a single function with variable no of kwargs that you sniffed in the body, and I don't think applying function overloading alone as OP has done solves the underlying problem.

BiteCode_dev said 4 days ago:

> My point was just that, even if for some reason you prefer function overloading, the restrictions needed to fit within the constraints of functool.singledispatch arguably lead to cleaner code than the complicated mess in the OP article.

Indeed. Less is more sometimes.

jakearmitage said 4 days ago:

Didn't know about @dataclass until now. Thank you.

oefrha said 4 days ago:

See also attrs http://www.attrs.org/ which is one of the main inspirations of py37 dataclass and is more feature-rich.

BiteCode_dev said 4 days ago:

Python 3.7+ only.

harel said 4 days ago:

Came here to say the same thing - so take my upvote sir.

Waterluvian said 4 days ago:

I love Python as a "hack it into a Frankenstein monster for fun" language because of how much power you have to get under the hood. For that reason I love articles like this.

But I'm not sure you'd ever want to implement overloading for practical uses. Just utilize optional arguments and some branching logic within the function. But even then, rethink how much you're trying to pack into a function.

lordgrenville said 4 days ago:

Yes, my reaction to this was "this is horrifying Python, never do this in real life!" But also fascination at how malleable and subvertible it is once you go deep enough into the internals.

iovrthoughtthis said 4 days ago:

You might enjoy ruby as well then!

BiteCode_dev said 4 days ago:

Remember that duck typing + default values + args + kwargs is usually more idiomatic than function overloading in python.

Don't try to code in python like you code in another language.

Also, think about what your API is conveying. Overloading may make it less clear.

The snippet from this article is a good example of when not to do it:

    def area (l, b): 
      return length * breadth;


    def area(r):
      return 3.14 * radius * radius
Here, you have 2 functions that do 2 fundamentally different things. One is calculating an area for a circle. The other one is for a rectangle. When you have 2 different signatures, it's often a sign the process is very different, and you should signal that in your API.

If you scan code using those, your brain cannot quickly parse it. It needs to process "area(), ok but of what, well, it has only x param, so I guess this is for z". The overloading gimmick has little value for the reader, but a real cost (not to mention in python it actually slows the run time)

The better solution is simply to make it explicitly 2 functions with good names:

    def rectangle_area (l, b): 
      return length * breadth;

Or, most likely, put them in separated namespaces (using a module or a class), which does the same.

    import circle

    circle.area(r)       
It's very easy to abuse overloading because it tickles our need for refactoring. But it's a tool to solve a problem, not an end by itself.
oefrha said 4 days ago:

There's really nothing wrong with having two different signatures, it's a pretty pythonic way to do things. It's harder to document for sure, but other than that hardly a problem in many cases.

This post unfortunately reads like one of those Java programmer discovers Python (except not really) posts, but for C++. In general if you find yourself inventing a new paradigm for a 30-year old language you just started working with, it's reason enough to doubt you're doing something suboptimal. However, in this case the author is actually very familiar with Python, so I'm kinda confused.

throwaway_pdp09 said 4 days ago:

The first part seems the wrong way to do it (treating it as an example). You would presumably overload area() on the object type, not on individual parameters representing the object's geometry. 'circle.area(r)' is even weirder - extracting radius just doesn't make sense here.

<SomeGeometricObject>.area() is right AFAICS.

Which I think is what you're getting at anyway.

But IMO overloading used right is cleaner than not having overloading.

BiteCode_dev said 4 days ago:

> 'circle.area(r)' is even weirder

It's just putting the original area() function in a module, not making it a method of a class. It's the simplest solution you can have, ever.

Adding an object is already more complex, and complexity needs to be justified. Just KISS if you can.

arunix said 4 days ago:

This seems like a lot of work for a result that isn't even very self documenting compared to some earlier efforts e.g.

Guido's post on multimethods: https://www.artima.com/weblogs/viewpost.jsp?thread=101605

Clojure style multimethods: https://adambard.com/blog/implementing-multimethods-in-pytho...

throwaway_pdp09 said 4 days ago:

I remember reading the multimethods post and thinking it was wrong as it does not dispatch on all arguments, as dylan would (and I believe closure would too) - correct me if I'm wrong though.

arunix said 3 days ago:

It does dispatch on the types of all arguments, ... that's the point of it.

Some sample code (I modified it to be commutative on the argument types):

https://gist.github.com/arunbear/caef7546d297c01fddbd3f67117...

goodside said 3 days ago:

This borders on spam. This is the third time you’ve submitted this post in the past two months and the feedback is no more positive this time.

Yes, you can do just about anything in Python. But single dispatch is usually a bad idea, and implementing it yourself instead of using the Python stdlib is always a bad idea.

There’s nothing wrong with showing off programming curiosities, but this isn’t presented in that context. The more often you post it, especially without heeding or engaging with feedback, the more annoying it becomes.

enricozb said 4 days ago:

If you want to "overload" a function. Do

    def area(shape):
      return shape.area()
and implement it appropriately in your type.
trymas said 4 days ago:

https://pypi.org/project/multidispatch/

IMHO, multiple dispatch is unpythonic and you should use it only if you really need it. Also OP's implementation is missing typing information and differentiates functions only by length of arguments. Though probably that's the reason I do not like this idea in python because it's not a statically typed language.

said 4 days ago:
[deleted]
lervag said 4 days ago:
whalesalad said 4 days ago:

Singledispatch from functools will often work in a lot of situations where you might want to do something like this: https://docs.python.org/3/library/functools.html

This is a cool post from the author, though. I interpret this as more of an exploration of Python under the hood versus being a recommended production solution.

Python certainly could use a facility to do that based on an arbitrary predicate fn versus type comparison, though.

Python is well suited for this.. maybe it is time to throw a PEP in the ring.

jstrong said 4 days ago:

"Yeah, but your scientists were so preoccupied with whether or not they could, they didn’t stop to think if they should." -Dr. Ian Malcolm

sleavey said 4 days ago:

Isn't this behavior in Python already via the functools module? Can't look it up right now but I'm pretty sure it's there.

joshuamorton said 4 days ago:

Functions.singledispatch allows overloading based on the type of the first argument, but doesn't allow complex overloads, like arg-count based or multi-type based.

emidln said 4 days ago:

This is not hard to write if you care about it (dispatching on data in addition to dispatching on type). Clojure-style multimethods with python-style type dispatch or data result dispatch can be had for under 150 lines. It's not particularly useful unless you are porting code or otherwise writing very unpythonic code.

mrkeen said 4 days ago:

If you allow both

  int area(int length, int breadth)
and

  float area(int radius)
to exist, then in the future you won't be able ask what the type of 'area' is. (If that's your kind of thing)
zomglings said 4 days ago:

You will, the type could be Union[int, float].

Alternatively, you could also use the abstract base classes in the numbers module - https://docs.python.org/3/library/numbers.html

numbers.Number may be a good choice.

Still, you are right, overloading like this may be okay as a thought exercise, but would raise some serious questions during a code review. :)

klhugo said 4 days ago:

Using fancy features does not make you a better programmer. It is the other way around.

mrkeen said 4 days ago:

Being a better programmer makes you use fancy features?

modo_ said 3 days ago:

Charitably interpreted: A better programmer knows using a fancy feature _can_ result in better code, but, also knows that fancy features usually aren't required to write good code (and that fancy features can turn good code into bad code when reached for too quickly)

detaro said 3 days ago:

duplicate, please don't repost so quickly: https://news.ycombinator.com/item?id=22340720

jl2718 said 3 days ago:

I would have expected polymorphism with type hints. Oh well.

agumonkey said 2 days ago:

next: type based dispatch

nzxt^2: predicate based dispatch