Re: PyMyth: Global variables are evil... WRONG!

2013-11-12 Thread jongiddy
On Tuesday, November 12, 2013 2:06:09 AM UTC, Rick Johnson wrote:

> 
> 
> 
>  Justifying Global Variables:
> 
> 
> 
> Globals are justified when they are used to communicate
> 
> information between scopes that otherwise were meant to be
> 
> mutually exclusive. One good example would be package sub-
> 
> modules.
> 

Can you please give an example where having a module provide a global variable 
would work better than any of:
1. providing a module function to change the operation of my module
2. providing a class with a method to change the operation of an instance
3. providing an additional parameter to module functions / instance methods to 
change operation
4. providing additional module functions / instance methods to perform 
different operations
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: PyMyth: Global variables are evil... WRONG!

2013-11-12 Thread jongiddy
On Tuesday, November 12, 2013 3:05:25 PM UTC, Rick Johnson wrote:
> 
> 
> 
> When i type "math.pi", i "feel" as though i'm accessing an
> 
> interface, BUT I'M NOT! 

I'm not sure where you get the feeling you're accessing an interface. If I 
typed this, I would feel like I was trying to change a fundamental constant.

You have just demonstrated that going into other modules and changing their 
attributes (whether they are variables, functions or classes) is generally a 
BAD idea, and I don't think I've ever seen anyone recommend doing this, except 
possibly as a workaround or for debugging purposes.

On the other hand, you initially suggested that modifying globals contained 
within namespaces (i.e. modules) is a good way to communicate between modules. 
That is, you suggested in your initial post that going into other modules and 
changing their attributes is a GOOD idea.

This inconsistency is why I was asking for a good example (i.e. a realistic 
example where the use of global variables provides the best solution).

Just because a tool allows you to do something does not make it a good idea. 
Try this paraphrase of your last post: Ladder designers act like standing on 
the top rung is SO evil, but then they give us ladders with a top rung, Are 
they just trying to fool themselves, or fool us? 
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: PyMyth: Global variables are evil... WRONG!

2013-11-12 Thread jongiddy
On Tuesday, November 12, 2013 5:00:37 PM UTC, Rick Johnson wrote: 
> 
> 
>1. Accept that globals are useful, and make them
> 
>   available through a "real" global syntax, not
> 
>   some attribute of a module that "appears" to be
> 
>   local, but in reality is global. Then prevent
> 
>   external mutation of module attributes directly,
> 
>   and instead, require that mutation must follow a
> 
>   contract defined by internal module "setter"
> 
>   functions.
> 
> 
> 
>2. Remain convinced that global mutation is evil
> 
>   and prevent mutation of all python module
> 
>   attributes. You can import modules, but you can't
> 
>   mutate their contents.

>From your first post, I take it you're not keen on option #2.

For #1, module globals are exactly the hierarchically namespaced globals that 
you desire in your first post, except they are variables, not get/set handlers 
(which is what I take you to mean by an "interface").

Why not create a PEP to provide handlers for module attributes? You could base 
it on PEP 213, which describes the same feature for classes.

As a bonus, this would trivially support option #2 (e.g. non-mutable math.pi) 
by raising an exception for the set operator.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Classic OOP in Python

2015-11-12 Thread jongiddy
On Thursday, 18 June 2015 12:21:29 UTC+1, Jason P.  wrote:
> 
> I'm aware of duck typing. The point in using interfaces is to be explicit 
> about the boundaries of a system.
> 
> Quite a red "Growing Object-Oriented Software, Guided by Tests", by the way. 
> In fact interfaces are key components in the style of building software they 
> propose, in good company with TDD.
> 
> Thx!


Late to the party, but I have added a package `jute` to PyPI which provides 
interface checking closer to the model used in Java (and other compiled OO 
languages). See https://github.com/jongiddy/jute
-- 
https://mail.python.org/mailman/listinfo/python-list


Uniform Function Call Syntax (UFCS)

2014-06-06 Thread jongiddy
The language D has a feature called Uniform Function Call Syntax, which allows 
instance methods to be resolved using function calls.

In Python terms, the call:

x.len()

would first check if 'x' has a method 'len', and would then look for a function 
'len', passing 'x' as the first argument.

The big wins are:

- the ability to override functions with more optimal class-specific 
implementations. (Of course, len() is a bad example, since we already have a 
way to override it, but there are other functions that do not have a special 
method).

- the readability of a.b().c().d() vs c(a.b()).d()

Here's a few links discussing the feature in D:
- First, a fairly gentle "this is cool" post: 
http://www.kr41.net/2013/08/27/uniform_function_call_syntax_in_d.html
- Second, an article from the Walter Bright, the creator of D: 
http://www.drdobbs.com/cpp/uniform-function-call-syntax/232700394

Has this been discussed or proposed before? I found PEP's 443 and 3124, which 
provide a form of function overloading, but not reordering.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Uniform Function Call Syntax (UFCS)

2014-06-08 Thread jongiddy
Thanks for the extensive feedback.  Here's my thoughts on how to address these 
issues.

On Saturday, 7 June 2014 20:20:48 UTC+1, Ian  wrote:
> 
> It's a nice feature in a statically typed language, but I'm not sure
> how well it would work in a language as dynamic as Python.  There are
> some questions that would need to be addressed.
> 
> 1) Where should the function (or perhaps callable) be looked for?  The
> most obvious place is the global scope.  I think it would be a bit too
> far-reaching and inconsistent with other language features to reach
> directly inside imported modules (not to mention that it could easily
> get to be far too slow in a module with lots of imports). As a result
> it would have to be imported using the "from module import function"
> syntax, rather than the somewhat cleaner "import module" syntax.
> 
> While there's nothing wrong with such imports, I'm not sure I like the
> thought of the language encouraging them any more than necessary.

It would only work on functions in scope. x.len() would only work if len(x) 
would work.  I actually think this would work better in Python than in D.  In 
D, "import module;" imports all the symbols from the module, so it is easier to 
invoke a function unexpectedly.  In Python, "import module" does not fill the 
namespace with lots of callable symbols, so UFCS would generally work with 
built-ins, local functions, or functions explicitly imported with "from module 
import...".   In this case, the need to use the "from module import fname" form 
can document that something unusual is happening.

> 2) What about getattr and hasattr?  If I call hasattr(x,
> "some_method"), and x has no such attribute, but there is a function
> in the global scope named "some_method", should it return True?  

> If we instead have hasattr return False though, and have getattr raise 
> an exception, then we have this very magical and confusing
> circumstance where getattr(x, 'method') raises an exception but
> x.method does not.  So I don't think that's really a good scenario
> either.

AS you suggest, the preferable route is that hasattr should return False.  The 
object clearly does not have that attribute.  It is a property of the current 
module that the object can use "instance.fname".  While the behaviour that 
hasattr("fname") returns False, but instance.fname works is an exception, and a 
function could be added to test this quickly, so new code that cares could use:
if hasattr(instance, "fname") or inscopecallable('fname'):

The bigger problem I find is reading other code that uses UFCS and not 
realising that a "method" is not actually a method of the class, but requires 
importing a module.  That can cause confusion when trying to use it in your own 
code.  However, the need to use "from module import fname" would at least link 
the method name and the module.

> Also the idea makes me nervous in the thought that an incorrect
> attribute access could accidentally and somewhat randomly pick up some
> object from the environment.  

As before, I think the limited number of strange callable objects in most 
modules in Python protects against this.  Of course, "from module import *" 
might cause problems, but that is already true.  You need to be extra careful 
doing this, and should only do it for modules when you have a reasonable 
understanding of their exported names.

> But if you want to experiment with the idea, here's a (lightly tested)
> mixin that implements the behavior:

Thanks for the headstart! I'll need to read up on descriptors to understand 
that last bit fully (when a function has a __get__ method).

One problem with your untested code, the superclasses would need to be checked 
before using UFCS, so the structure is:

try:
return super().__getattr__(attr)
except AttributeError:
# resolve using UFCS
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Uniform Function Call Syntax (UFCS)

2014-06-08 Thread jongiddy
On Sunday, 8 June 2014 02:27:42 UTC+1, Gregory Ewing  wrote:
> 
> Also it doesn't sit well with Python's "one obvious
> way to do it" guideline, because it means there are
> *two* equally obvious ways to call a function.

This provides a way to do something new (add class-optimized implementations 
for existing general-purpose functions). It also adds significant readability 
improvements by putting function-call chains in order.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Uniform Function Call Syntax (UFCS)

2014-06-08 Thread jongiddy
On Sunday, 8 June 2014 02:27:42 UTC+1, Gregory Ewing  wrote:
> 
> Also it doesn't sit well with Python's "one obvious
> way to do it" guideline, because it means there are
> *two* equally obvious ways to call a function.

Actually, one of the best arguments against introducing UFCS is that Python 
currently provides two equivalent ways to check if an instance has an 
attribute: ask-permission using hasattr and ask-forgiveness using 
AttributeError.

On the negative side, these currently equivalent (aside from performance) 
techniques could give different results using UFCS, potentially breaking some 
code.

On the positive side, that means the proposal would add one "two ways to do 
something" and eliminate another "two ways to do something", giving a net Zen 
of Python effect of zero.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Uniform Function Call Syntax (UFCS)

2014-06-08 Thread jongiddy
On Sunday, 8 June 2014 15:59:14 UTC+1, Roy Smith  wrote:
 
> Why?  I assume a language which promoted the global namespace to be in 
> the attribute search path (which, as far as I can tell, is what we're 
> talking about here) would implement hasattr and raising AttributeError 
> in a consistent way.

It's slightly different. Although I used len() as an example, the idea is to 
allow any function to be used in this way, including local symbols.

e.g. I could define:

def squared(x):
return x * x

i = 3
i.squared() => 9

j = AClassThatImplements__mul__()
j.squared() => whatever j * j returns

but also:
class AnotherClass:
def __mul__(self, other):
...
def squared(self):
return specialised_method_for_calculating_squares()

k = AnotherClass()
k.squared() => calls method, not function

In this case, there is a problem with letting hasattr('squared') return True 
for these first two instances.  See Ian's post for a description of the problem.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Uniform Function Call Syntax (UFCS)

2014-06-08 Thread jongiddy
On Sunday, 8 June 2014 13:06:08 UTC+1, Paul Sokolovsky  wrote:
> 
> Getting x.foo() to call foo(x) is what's bigger problem, which has
> serious performance and scoping confusion implications, as discussed in
> other mails.

The performance hit will only occur when the attribute access is about to throw 
an AttributeError.  Successful attribute accesses would be just as fast as 
before.  And the cost of a symbol lookup is usually considered cheap compared 
to a thrown exception, so I don't believe there is a serious performance 
implication.

As to the scoping confusion, I repeat that Python benefits from the fact that 
most modules will only have the builtins and local functions to worry about.  
This is a small enough space for users to manage.  There's no surprises waiting 
to occur when the user adds or removes normal imports (a problem that can occur 
in D).

> > It also adds
> > significant readability improvements by putting function-call chains
> > in order.
>
> Not sure what exactly you mean, but the order is usually pretty obvious
> - Python follows mathematical notation for function calls, and OO
> standard notation for method calls, one known from primary school,
> another from secondary (hopefully). They can be reordered with
> parentheses, which is also well-known basic math technique.

A contrived example - which of these is easier to understand?

from base64 import b64encode

# works now
print(b64encode(str(min(map(int, f.readlines()), key=lambda n: n % 10)), b'?-'))

# would work with UFCS
f.readlines().map(int).min(key=lambda n: n % 10).str().b64encode(b'?-').print()

You can read the second form left to right, and arguments like b64encode's 
b'?-' are near the function call, making it a lot more obvious with which 
function this obscure argument is used.

Note, I'm not suggesting either of these examples is good programming, but the 
same problem does occur in more reasonable scenarios - I just made this example 
a little extreme to emphasise the readability benefits.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Uniform Function Call Syntax (UFCS)

2014-06-08 Thread jongiddy
On Sunday, 8 June 2014 17:24:56 UTC+1, jongiddy  wrote:

> # would work with UFCS
> f.readlines().map(int).min(key=lambda n: n % 
> 10).str().b64encode(b'?-').print()

Ooops - map is the wrong way round to support UFCS in this case.  However, with 
UFCS, I could fix this by changing it to smap, and defining:

def smap(seq, func):
   return map(func, seq)
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Uniform Function Call Syntax (UFCS)

2014-06-08 Thread jongiddy
On Sunday, 8 June 2014 18:24:28 UTC+1, Ian  wrote:
> 
> But that would all be done in getattr, so I don't think it affects
> hasattr's implementation at all.  Since hasattr doesn't push anything
> onto the stack, getattr doesn't have to care whether it was called
> directly from Python or indirectly via getattr; either way the scope
> it needs is just the top frame of the stack.
> 
> Could be a different matter in other implementations, though.

In CPython, the UFCS would not be done in PyObject_GetAttr() as that would 
affect hasattr() as well. Instead, it would be implemented in the bytecode for 
LOAD_ATTR. If LOAD_ATTR was about to return an AttributeError, e.g. for [].len, 
it would perform the equivalent of a LOAD_NAME operation, with the difference 
that if the name is not found or is not callable, it returns AttributeError 
instead of NameError.

If the name is found, then it would return something: for [].len, it would 
return the len() function wrapped to know that it's first argument was the 
list, which might be done by creating a fake Method object, as shown in Ian's 
code.

But getattr([], 'len') and hasattr([], 'len') would both return False.

I'm beginning to think it is too un-Pythonic - too much implicitness, unless it 
can be spelt differently, something like [].len(_) or [].len(...) to explicitly 
indicate that it plans to call a function, but might call a method if one is 
available.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Uniform Function Call Syntax (UFCS)

2014-06-08 Thread jongiddy
On Monday, 9 June 2014 04:44:22 UTC+1, Chris Angelico  wrote:
> This could be solved, though, by having a completely different symbol
> that means "the thing on my left is actually the first positional
> parameter in the function call on my right", such as in your example:
> 
> > plus(1, 2) | divide(2)
> 
> This would be absolutely identical to:
> 
> divide(plus(1, 2), 2)
> 
> Maybe you could even make it so that:
> 
> plus(1, 2) x=| divide(y=2)
> 
> is equivalent to
> 
> divide(x=plus(1, 2), y=2)
> 
> for the sake of consistency, and to allow the pipeline to inject
> something someplace other than the first argument.
> 
> I'm not sure whether it'd be as useful in practice, though. It would
> depend partly on the exact syntax used. Obviously the pipe itself
> can't be used as it already means bitwise or, and this needs to be
> really REALLY clear about what's going on. But a data-flow notation
> would be of value in theory, at least.

Perhaps a pipeline symbol plus an insertion marker would work better in Python:

plus(1, 2) ~ divide(x=^, y=2)

f.readlines() ~ map(int, ^) ~ min(^, key=lambda n: n % 10).str() ~ 
base64.b64encode(^, b'?-') ~ print(^)

Stdio.read_file("foo.jpg") ~ Image.JPEG_decode(^).autocrop().rotate(0.5).grey() 
~ Image.PNG_encode(^) ~ Stdio.write_file("foo.png", ^)

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Uniform Function Call Syntax (UFCS)

2014-06-09 Thread jongiddy
So, just to summarise the discussion:

There was some very mild support for readable pipelines, either using UFCS or 
an alternative syntax, but the Pythonic way to make combinations of function 
and method applications readable is to assign to variables over multiple lines. 
 Make the code read down, not across.

The idea that a class method could override a function using UFCS didn't get 
much traction. From Zen of Python, "explicit is better than implicit" means no 
differences in behaviour, depending on context. The fact that x.y and 
x.__getattr__ may behave differently under UFCS is also a problem. Since 
hasattr testing and AttributeError catching are both commonly used now, this 
could cause real problems, so could probably not be changed until Python 4.

Finally, Gilbert & Sullivan are definitely due a revival.
-- 
https://mail.python.org/mailman/listinfo/python-list