On Thu, Sep 17, 2020 at 7:07 PM Inada Naoki <songofacandy@gmail.com> wrote:
On Fri, Sep 18, 2020 at 12:07 AM Paolo Lammens <lammenspaolo@gmail.com> wrote:
> Besides, I don't understand what the downside of overloading is, apart from purism (?).
I am one of who are conservative about overloading. I agree this is
purism, but I want to explain behind of this purism.

Thank you -- it is key to understanding where we should go with this feature.
 
For example,

* str subtype can implement read/write method. It is both of PathLike
and file-like.
* File subtype can implement `.__fspath__`. It is both of PathLike and File.

I see the issue here, but isn't that inherent in duck typing? It's not specific to overloading. In fact the current implementation of json.load() simply calls the .read() in the object passed in. If it does not have a read method, you get an AttributeError. IF someone where to pass in a string subclass with a read method, and that method returned the right thing, it would "just work". Here's an example:

In [5]: class ReadableString(str):
   ...:     def read(self):
   ...:         return self                                                                In [6]: rs = ReadableString('{"some": "json"}')                                

In [7]: rs                                                                      
Out[7]: '{"some": "json"}'

In [8]: json.load(rs)                                                          
Out[8]: {'some': 'json'}

This is the whole point of dynamic, duck typing yes?

In a sense, the json.load() is already "overloaded" to take anything with a read() method that returns a string containing JSON.

If I were to overload load() to allow a path-like object, I would probably do:

def load(f_or_p, *, cls=None, object_hook=None, parse_float=None,
         parse_int=None, parse_constant=None, object_pairs_hook=None, **kw):

    try:
        fp = open(f_or_p, 'r', encoding="utf-8")
    except TypeError:
        fp = f_or_p

    return loads(fp.read(),
                 cls=cls, object_hook=object_hook,
                 parse_float=parse_float, parse_int=parse_int,
                 parse_constant=parse_constant,
                 object_pairs_hook=object_pairs_hook, **kw)

In this case, it would work on anything that either could be used in open() or had a read() method.

Is that really much different?

On the other hand, in case of Python, there are no compiler/VM support
for overloading, because Python is duck-typed language.

* `load(f, ...)` uses `f.read()`
* `dump(f, ...)` uses `f.write()`
* `loadf(path, ..)` and `dumpf(path, ...)` uses `open(path, ...)`

This is so natural design for duck-typed language.

or:
* `load(f, ...)` uses `f.read()` or open(f)

I can see how that is a bit more complicated, but don't see how it makes anyone's life worse or more confusing.

-CHB
 


--
Christopher Barker, PhD

Python Language Consulting
  - Teaching
  - Scientific Software Development
  - Desktop GUI and Web Development
  - wxPython, numpy, scipy, Cython