Re: Python is DOOMED! Again!

2015-02-08 Thread Ian Kelly
On Sat, Feb 7, 2015 at 5:45 PM, Albert van der Horst
alb...@spenarnc.xs4all.nl wrote:
 It is too bad `` - ''  as a token is now taken.
 I wanted to propose to replace the ternary syntax
 lambda  ..  : ..
 by a regular operator
 .. - ..
 then we could have
 x - x**2
 instead of
 lambda x : x**2

Well, I don't think the existing syntax is incompatible with your
proposal. As it is, the - token can only appear after the argument
list of a def statement, so there would be no grammatical ambiguity. I
do think that such a proposal is unlikely to gain wide support though.

 Moreover the value of a function would be a lambda

 not
 def square(x): x**2
 but
 square = x-x**2

This would be an anti-pattern. The def statement associates the name
square with the function's __name__ attribute, which is useful for
debugging and introspection. The proposed assignment statement does
not.

 mult  = x,y -
result = 0
for i in range(x):
   result +=y
return result

I don't like this at all. I read x - x**2 as denoting a mapping
from a bound variable to an expression. A whole function body just
feels wrong here.

 doing away with the ternary operator def

def is a statement, not an operator.

 replacing it by two binary operators, one of them (=) being thoroughly 
 familiar.

= is also not an operator.

 Also name:str is the wrong order.

I disagree; name: type is linguistically correct, with the colon
denoting that what comes after describes what comes before. Without
the colon, the opposite order would make more sense.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Python is DOOMED! Again!

2015-02-08 Thread Marko Rauhamaa
Ian Kelly ian.g.ke...@gmail.com:

 On Sat, Feb 7, 2015 at 5:45 PM, Albert van der Horst
 alb...@spenarnc.xs4all.nl wrote:
 x - x**2
 instead of
 lambda x : x**2

 Well, I don't think the existing syntax is incompatible with your
 proposal. As it is, the - token can only appear after the argument
 list of a def statement, so there would be no grammatical ambiguity. I
 do think that such a proposal is unlikely to gain wide support though.

I also don't think Python is in the need of any enhancement in this
area.

 not
 def square(x): x**2
 but
 square = x-x**2

 This would be an anti-pattern. The def statement associates the name
 square with the function's __name__ attribute, which is useful for
 debugging and introspection. The proposed assignment statement does
 not.

A good point. However, Guile (Scheme) has some builtin magic:

(define (f x) x)
(procedure-name f)
   $1 = f
(define g (lambda (x) x))
(procedure-name g)
   $2 = g
(define h g)
(procedure-name h)
   $3 = g

 mult  = x,y -
result = 0
for i in range(x):
   result +=y
return result

 I don't like this at all.

The main problem is the missing colon, a cornerstone of Python syntax.

You could have:

   mult  = x,y -:
  result = 0
  for i in range(x):
 result +=y
  return result

 I read x - x**2 as denoting a mapping from a bound variable to an
 expression. A whole function body just feels wrong here.

I don't think syntax like this is in any way wrong. It's just completely
unnecessary given that we have def.

Some people are trying to make Scheme more Python-like, others are
trying to make Python more Scheme-like. I think you should not dilute
the idiomatic core of a programming language. When in Python, program in
Python, when in Scheme, program in Scheme...


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


Re: Python is DOOMED! Again!

2015-02-08 Thread Steven D'Aprano
Chris Angelico wrote:

 On Sun, Feb 8, 2015 at 11:45 AM, Albert van der Horst
 alb...@spenarnc.xs4all.nl wrote:
 def square(x): x**2
 but
 square = x-x**2

 or

 mult  = x,y -
result = 0
for i in range(x):
   result +=y
return result

 doing away with the ternary operator def

 def .. ( .. ) : ..

 replacing it by two binary operators, one of them (=) being thoroughly
 familiar.
 
 Thing is, def isn't just assignment. It also takes the name and
 stores it in the object. There's a distinct and visible difference
 between:
 
 def square(x): return x**2
 
 and
 
 def func(x): return x**2
 square = func
 
 So if you were to use your alternate syntax everywhere, you'd
 basically be throwing away all the benefits of def over lambda.


If this were syntax, then the compiler could just as easily set the function
name from - as from def. Lambda has the limitations that it has because it
is an expression, not because of magical def properties.

I think it is a total waste of good syntax to limit a hypothetical -
operator to a mere replacement for def. It would be much more interesting
for pattern-matching, rather like Haskell:

fact 0 = 1
fact n = n*fact(n-1)


That is more or less equivalent to pseudocode:

def fact(arg):
if arg == 0: return 1
if arg matches n: return n*fact(n-1)


What does it mean to match n? Haskell can infer that n must be an integer.
(It might even be able to infer that n must be a *positive* integer. I'd
test it myself except I'm lazy.) So if it receives an integer argument
which isn't 0, it will match n.

Because indentation in Python is significant, we could drop the repeated use
of the function name by indenting the individual patterns, and use type
annotations to specify different types:

fact 0 - 1
n:int - n*fact(n-1)
x:float - math.gamma(x-1)


As I said, this is not a thought-out proposal, just some ideal musings, but
it seems a crying shame to waste a potential pattern match operator for a
mere abbreviation to def.



-- 
Steven

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


Re: Python is DOOMED! Again!

2015-02-08 Thread Chris Angelico
On Sun, Feb 8, 2015 at 6:55 PM, Steven D'Aprano
steve+comp.lang.pyt...@pearwood.info wrote:
 If this were syntax, then the compiler could just as easily set the function
 name from - as from def. Lambda has the limitations that it has because it
 is an expression, not because of magical def properties.

True, it could, but it would be odd that what looks like assignment
and an expression is actually magical syntax. But it might be nice to
have something that functions as lambda currently does, unless it
detects that it's being assigned directly to a name, in which case the
magic kicks in and it gets a name.

square = x-x**2 # Name is square
operator[square] = x-x**2 # Maybe?
create_operator(square, x-x**2) # Name is lambda

But I suspect that this would create some hairy grammatical quirks.
Still, as an alternate syntax for creating a lambda function, it
seems at least plausible.

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


Re: Python is DOOMED! Again!

2015-02-07 Thread Albert van der Horst
In article mailman.17951.1421906568.18130.python-l...@python.org,
Ethan Furman  et...@stoneleaf.us wrote:
-=-=-=-=-=-

On 01/21/2015 08:30 PM, Steven D'Aprano wrote:

 So what is this unspeakable, nightmarish, cryptic abomination going to look
 like? Here's an example from PEP 484:

 def greeting(name: str) - str:
 return 'Hello ' + name


 I don't know about you, but I think anyone who cannot read that and intuit
 that argument `name` is a string and the return result is also a string

There is nothing inherently intuitive about that syntax.  The : makes it
look like a dictionary (but it isn't) and the
- looks like a pointer to something (but it isn't).

It is too bad `` - ''  as a token is now taken.
I wanted to propose to replace the ternary syntax
lambda  ..  : ..
by a regular operator
.. - ..
then we could have
x - x**2
instead of
lambda x : x**2

Moreover the value of a function would be a lambda

not
def square(x): x**2
but
square = x-x**2

or

mult  = x,y -
   result = 0
   for i in range(x):
  result +=y
   return result

doing away with the ternary operator def

def .. ( .. ) : ..

replacing it by two binary operators, one of them (=) being thoroughly familiar.

It is horrifying to see that def is now becoming a quaternary operator

def .. ( .. ) -- .. : ..


Also name:str is the wrong order.

I would propose to use :: to prevent confusion.
Then I would allow  type:: in front of all objects everywhere
to trigger a warning if at that point the objects is not of the right type.
I think it is quite natural that float: sets the expectation
that a float is coming.

float:: x = get_some_crap_from_an obscure_windows_box( a, B, c,
   a_lighter, some_list, REAL_ISB_MASK )


 is probably going to have bigger troubles with Python than just type-hinting.

Yup, true -- I do find writing meta-classes takes extra work.  ;)

--
~Ethan~



-=-=-=-=-=-
[Attachment type=application/pgp-signature, name=signature.asc]
-=-=-=-=-=-
-- 
Albert van der Horst, UTRECHT,THE NETHERLANDS
Economic growth -- being exponential -- ultimately falters.
albert@spearc.xs4all.nl =n http://home.hccnet.nl/a.w.m.van.der.horst

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


Re: Python is DOOMED! Again!

2015-02-07 Thread Chris Angelico
On Sun, Feb 8, 2015 at 11:45 AM, Albert van der Horst
alb...@spenarnc.xs4all.nl wrote:
 def square(x): x**2
 but
 square = x-x**2

 or

 mult  = x,y -
result = 0
for i in range(x):
   result +=y
return result

 doing away with the ternary operator def

 def .. ( .. ) : ..

 replacing it by two binary operators, one of them (=) being thoroughly 
 familiar.

Thing is, def isn't just assignment. It also takes the name and
stores it in the object. There's a distinct and visible difference
between:

def square(x): return x**2

and

def func(x): return x**2
square = func

So if you were to use your alternate syntax everywhere, you'd
basically be throwing away all the benefits of def over lambda.

Now, you may well be able to justify and implement an alternative for
lambda that works this way. (And it's probably already been done, too.
Maybe using the actual symbol λ rather than -.) But I don't think
you'll get anywhere with def.

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


Re: dunder-docs (was Python is DOOMED! Again!)

2015-02-03 Thread Steven D'Aprano
Gregory Ewing wrote:

 Marko Rauhamaa wrote:
 For (almost) all practical purposes, that is the Python way as well. If
 object instantiation (conceptually) copied the class's methods into the
 object's dict, you'd get the semantics I'm looking for.
 
 If things worked the way you want, it would be
 impossible to store a function in an instance
 attribute and get it out again *without* it
 being treated as a method and getting 'self'
 added to its arguments. That would be a
 considerable nuisance when dealing with
 callbacks and the like.


Not impossible, just inconvenient. Assuming that the descriptor protocol 
runs on access to instance attributes as well as class attribute, the 
solution is to use a custom descriptor to return the bare function.

class function(object):
def __init__(self, func):
self.func = func
def __get__(self, obj, cls=None):
return self.func


instance.callback = function(mycallback)


Or you can possibly use staticmethod.



-- 
Steve

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


Re: dunder-docs (was Python is DOOMED! Again!)

2015-02-03 Thread Steven D'Aprano
Gregory Ewing wrote:

 Marko Rauhamaa wrote:
 Right now Python generates the trampoline from the class prototype every
 time you call a method. If the semantics allowed, you could create the
 trampoline at instantiation time (for better or worse). That way, the
 problem you seem to be referring to wouldn't materialize.
 
 Sorry, I misinterpreted what you were suggesting.
 
 You seem to be suggesting an optimisation that pre-creates
 bound methods when the instance is created. Keeping a
 cache of bound methods would achieve the same thing
 without changing the semantics. I think CPython
 might already be doing that, but I'm not sure.

It's not.

py class K(object):
... def f(self): pass
... 
py k = K()
py k.f is k.f
False



-- 
Steve

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


Re: dunder-docs (was Python is DOOMED! Again!)

2015-02-03 Thread Gregory Ewing

Steven D'Aprano wrote:

Gregory Ewing wrote:


If things worked the way you want, it would be
impossible to store a function in an instance
attribute and get it out again *without* it
being treated as a method


Not impossible, just inconvenient... the 
solution is to use a custom descriptor


But then it's not a plain function any more. Obviously
you can wrap your function in something else, but
that's the nuisance I was talking about.

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


Re: dunder-docs (was Python is DOOMED! Again!)

2015-02-03 Thread Rick Johnson
On Tuesday, February 3, 2015 at 4:05:57 PM UTC-6, Ian wrote:
 On Tue, Feb 3, 2015 at 10:40 AM, Steven D'Aprano wrote:
  AT LONG LAST THE LIGHT DAWNS! THE PENNY DROPS!
 
 Careful there, you're starting to sound like Ranting Rick. ;-)

Ha! My meme's are far more catchy and intellectual. But as they say, intimation 
*IS* the ultimate form of flattery!
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: dunder-docs (was Python is DOOMED! Again!)

2015-02-03 Thread Gregory Ewing

Steven D'Aprano wrote:

Er, perhaps code injection is not the best name for this, on account of it 
also being the name for a security vulnerability anti-pattern:


I'm talking about a variety of dependency injection where you either add an 
entirely new method to an instance, or give the instance it's own custom 
method overriding the one declared in the class.


I think the term you're after is monkeypatching.

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


Re: dunder-docs (was Python is DOOMED! Again!)

2015-02-03 Thread Gregory Ewing

Steven D'Aprano wrote:
I'm arguing with those who insist that objects of type MethodType aren't 
methods, and objects of type FunctionType aren't functions but methods, 
except when they are, based on that simplified, incomplete glossary entry.


I'm not arguing that based on the glossary entry. I'm
arguing it based on my experience of how the term 'method'
is used in the Python community.

 I can inject methods into a class or even an instance. If you
think that methods *must* be defined in the class body, you're missing out 
on a very powerful technique.


I'm willing to accept 'method' as a legitimate term for
a function injected into a class body, *if it's used
as a method* once it gets there.

This is what you don't seem to understand. It's not
about the type of the object. You can't write a piece
of code to test whether a given object is a method or
not, because it's not an intrinsic property of the
object. It's a matter of how it's *used*.

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


Re: dunder-docs (was Python is DOOMED! Again!)

2015-02-03 Thread Marko Rauhamaa
Chris Angelico ros...@gmail.com:

 On Tue, Feb 3, 2015 at 9:38 PM, Marko Rauhamaa ma...@pacujo.net wrote:
 It's slightly sad that Python exposes the two-level attribute lookup.
 It would be more elegant if, conceptually, all methods were retrieved
 from the object's attribute dict. That way, the class would be simply
 a mundane optimization trick instead of a metaphysical entity.

 That's the ECMAScript model of classes - prototype-based. It's not
 Python's. There are many different ways to do things.

For (almost) all practical purposes, that is the Python way as well. If
object instantiation (conceptually) copied the class's methods into the
object's dict, you'd get the semantics I'm looking for.


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


Re: dunder-docs (was Python is DOOMED! Again!)

2015-02-03 Thread Gregory Ewing

Steven D'Aprano wrote:

Both K.f and K.g are methods, even though only one meets the definition
given in the glossary. The glossary is wrong.

Or rather, it is not so much that it is *wrong*, but that it is incomplete
and over-simplified.


I agree with that; a more complete definition would be
a function that is found in a class as a result of an
attribute lookup on an instance of that class.


I defined a method:

py from types import MethodType
py type(instance.f) is MethodType
True


Being of type MethodType is not the defining characterisic
of a method. MethodType is actually misnamed; an instance
of MethodType is *not* a method, in the same way that an
eggcup is not an egg.

A better name would be MethodWrapper, but that still
doesn't mean that anything you wrap with it is a method.
An eggcup *usually* contains an egg, but it could
contain something else.


instance.f is a method by the glossary definition.


Not by the glossary definition as written, since the
function you wrapped in a MethodType was not defined
inside a class body.

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


Re: dunder-docs (was Python is DOOMED! Again!)

2015-02-03 Thread Ian Kelly
On Tue, Feb 3, 2015 at 10:40 AM, Steven D'Aprano
steve+comp.lang.pyt...@pearwood.info wrote:
 AT LONG LAST THE LIGHT DAWNS! THE PENNY DROPS!

Careful there, you're starting to sound like Ranting Rick. ;-)
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: dunder-docs (was Python is DOOMED! Again!)

2015-02-03 Thread Chris Angelico
On Wed, Feb 4, 2015 at 4:40 AM, Steven D'Aprano
steve+comp.lang.pyt...@pearwood.info wrote:
 given that the glossary need not be 100% complete and definitive, function
 defined inside a class body is close enough to the truth.

* This *

We are arguing, not about an element in a formal grammar, but about a
glossary entry. If one of my Python students asks me, What's a
method?, I'm not going to go into a technical explanation like this;
I want to answer with a single sentence that covers the bit that
matters. (Though I'd probably define it from the other perspective -
it's an object attribute that you can call, perhaps - but if the
question came from a class definition, a function defined inside a
class would be fine.)

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


Re: dunder-docs (was Python is DOOMED! Again!)

2015-02-03 Thread Gregory Ewing

Steven D'Aprano wrote:


In Python 2, they are methods. In Python 3, they are functions, and aren't
converted into methods until you access them via the instance:


They're methods in both cases. They don't have to be
converted into methods; they already are, by virtue
of their location and intended usage.

The wrapper that gets created on attribute access isn't
the method (despite being called MethodType!) -- the
method is the function that it wraps. The wrapper is
just part of the machinery that passes the self argument
to the method.

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


Re: dunder-docs (was Python is DOOMED! Again!)

2015-02-03 Thread Steven D'Aprano
Devin Jeanpierre wrote:

 On Mon, Feb 2, 2015 at 6:07 AM, Steven D'Aprano
 steve+comp.lang.pyt...@pearwood.info wrote:
 Run this code:

 # === cut ===

 class K(object):
 def f(self): pass

 def f(self): pass

 instance = K()
 things = [instance.f, f.__get__(instance, K)]
 from random import shuffle
 shuffle(things)
 print(things)

 # === cut ===


 You allege that one of these things is a method, and the other is not. I
 challenge you to find any behavioural or functional difference between
 the two. (Object IDs don't count.)

 If you can find any meaningful difference between the two, I will accept
 that methods have to be created as functions inside a class body.
 
 In this particular case, there is none. What if the body of the
 method was super().f() ?

What of it? The zero-argument form of super() is a compiler hack documented
as only working inside classes. Most methods don't even use super *at all*,
let alone the Python3-only zero argument form. Surely you cannot be serious
that the defining characteristic of what is or is not a method is whether
or not an explicit hack works?

But you know, I can duplicate the compiler hack and still use the
zero-argument form of super in a method defined on the outside of the
class.


py class A(object):
... pass
...
py def magic():  # Don't do this at home!
... __class__ = A
... def f(self):
... __class__
... return super()  # Magic zero-argument form.
... return f
...
py A.f = magic()
py a = A()
py a.f()
super: class 'A', A object


So to answer your question:

What if the body of the method was super().f() ?


It would still work correctly. Because Python is just that awesome.


 Some methods can be defined outside of the body and still work exactly
 the same, but others won't.

Some methods can be defined outside of the body.

AT LONG LAST THE LIGHT DAWNS! THE PENNY DROPS!

If some methods can be defined outside of the body of a class, then being
defined inside the body of a class does not define a method.

You say some methods, but the reality is that *all methods* can do so.
Even if they use super. Even if they use the zero-argument form of super
(although that one is rather inconvenient).

The class statement is syntactic sugar for calling type(name, bases, ns):

class A(bases):
x = 100
f = lambda self: self.x + 1

is equivalent to:

A = type(A, bases, {'x': 100, 'f': lambda self: self.x + 1})

*ANY* class you create with the class statement can be created (less
conveniently) with type (or the appropriate metaclass).

Obviously the class statement form is convenient because it allows you to 
define the items of the namespace in place, which you can't always do using
type itself. With type, you may have to prepare the items first, insert
them into a dict, and pass it as the namespace argument. It also offers
convenient syntax for changing the metaclass. Most importantly, it is
simply more readable and nicer to be able to define the methods indented
inside the class statement. This is the natural way to define classes, and
given that the glossary need not be 100% complete and definitive, function
defined inside a class body is close enough to the truth.

But it is not *the whole truth*.


Not only can methods be defined outside of a class, but with a little
preparation functions defined inside of classes can remain functions.

py class function(object):
... def __init__(self, func):
... self.func = func
... def __get__(self, obj, cls=None):
... return self.func
...
py class B(object):
... @function
... def f(x, y):  # No self.
... return x + y
...
py B.f(23, 42)
65
py b = B()
py b.f(23, 42)
65
py type(b.f)
class 'function'


(Alternatively, I could change the metaclass, although that's trickier.
Using a custom descriptor is just too easy.)


 Otherwise you are reduced to claiming that there is some sort of
 mystical, undetectable essence or spirit that makes one of those two
 objects a real method and the other one a fake method, even though they
 have the same type, the same behaviour, and there is no test that can
 tell you which is which.
 
 It isn't mystical. There are differences in semantics of defining
 methods inside or outside of a class that apply in certain situations
 (e.g. super(), metaclasses). You have cherrypicked an example that
 avoids them.

Out of the millions, billions of methods people write, how many do you think
use super? 1% 0.1%? 10%?

And you accuse *me* of cherrypicking.

Not that it matters. As I have shown, even the zero argument form of super
can be used.



-- 
Steven

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


Re: dunder-docs (was Python is DOOMED! Again!)

2015-02-03 Thread Chris Angelico
On Wed, Feb 4, 2015 at 10:32 AM, Marko Rauhamaa ma...@pacujo.net wrote:
 No, I'm saying Python should behave differently.

 Python:

 class A:
...def f(self):
...   print(f)
...def g(self):
...   print(g)
...
 a = A()
 a.__class__.f = a.__class__.g
 a.f()
g

 In my preferred semantics, a.f() would print

 a.f()
f

Yeeeouch. So either it has to actually copy everything in on
instantiation (stupid cost for the tiny chance that it'll actually
ever matter), or else have some kind of versioning that means that it
knows that 'a' was created from the pre-changed class.

What's the advantage?!?

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


Re: dunder-docs (was Python is DOOMED! Again!)

2015-02-03 Thread Mark Lawrence

On 03/02/2015 23:32, Marko Rauhamaa wrote:

Gregory Ewing greg.ew...@canterbury.ac.nz:


You seem to be suggesting an optimisation that pre-creates bound
methods when the instance is created. Keeping a cache of bound methods
would achieve the same thing without changing the semantics. I think
CPython might already be doing that, but I'm not sure.


No, I'm saying Python should behave differently.

Python:

 class A:
...def f(self):
...   print(f)
...def g(self):
...   print(g)
...
 a = A()
 a.__class__.f = a.__class__.g
 a.f()
g

In my preferred semantics, a.f() would print

 a.f()
f


IMHO as clear as mud.

--
My fellow Pythonistas, ask not what our language can do for you, ask
what you can do for our language.

Mark Lawrence

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


Re: dunder-docs (was Python is DOOMED! Again!)

2015-02-03 Thread Gregory Ewing

Marko Rauhamaa wrote:

Right now Python generates the trampoline from the class prototype every
time you call a method. If the semantics allowed, you could create the
trampoline at instantiation time (for better or worse). That way, the
problem you seem to be referring to wouldn't materialize.


Sorry, I misinterpreted what you were suggesting.

You seem to be suggesting an optimisation that pre-creates
bound methods when the instance is created. Keeping a
cache of bound methods would achieve the same thing
without changing the semantics. I think CPython
might already be doing that, but I'm not sure.

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


Re: dunder-docs (was Python is DOOMED! Again!)

2015-02-03 Thread Marko Rauhamaa
Gregory Ewing greg.ew...@canterbury.ac.nz:

 You seem to be suggesting an optimisation that pre-creates bound
 methods when the instance is created. Keeping a cache of bound methods
 would achieve the same thing without changing the semantics. I think
 CPython might already be doing that, but I'm not sure.

No, I'm saying Python should behave differently.

Python:

class A:
   ...def f(self):
   ...   print(f)
   ...def g(self):
   ...   print(g)
   ... 
a = A()
a.__class__.f = a.__class__.g
a.f()
   g

In my preferred semantics, a.f() would print

a.f()
   f


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


Re: dunder-docs (was Python is DOOMED! Again!)

2015-02-03 Thread Devin Jeanpierre
On Mon, Feb 2, 2015 at 6:07 AM, Steven D'Aprano
steve+comp.lang.pyt...@pearwood.info wrote:
 Run this code:

 # === cut ===

 class K(object):
 def f(self): pass

 def f(self): pass

 instance = K()
 things = [instance.f, f.__get__(instance, K)]
 from random import shuffle
 shuffle(things)
 print(things)

 # === cut ===


 You allege that one of these things is a method, and the other is not. I
 challenge you to find any behavioural or functional difference between the
 two. (Object IDs don't count.)

 If you can find any meaningful difference between the two, I will accept
 that methods have to be created as functions inside a class body.

In this particular case, there is none. What if the body of the
method was super().f() ?

Some methods can be defined outside of the body and still work exactly
the same, but others won't.

 Otherwise you are reduced to claiming that there is some sort of mystical,
 undetectable essence or spirit that makes one of those two objects a
 real method and the other one a fake method, even though they have the same
 type, the same behaviour, and there is no test that can tell you which is
 which.

It isn't mystical. There are differences in semantics of defining
methods inside or outside of a class that apply in certain situations
(e.g. super(), metaclasses). You have cherrypicked an example that
avoids them.

If one wants to say A method can (...) by using super(), then
methods must be defined to only exist inside of class bodies.

Obviously, once you construct the correct runtime values, behavior
might be identical. The difference is in whether you can do different
things, not in behavior.

 For an example we can all agree on, this is not an instance of
 collections.Iterable, but the docs claim it is iterable:
 https://docs.python.org/2/glossary.html#term-iterable

 class MyIterable(object):
 def __getitem__(self, i): return i

 Iterable is a generic term, not a type. Despite the existence of the
 collections.Iterable ABC, iterable refers to any type which can be
 iterated over, using either of two different protocols.

 As I said above, if you wanted to argue that method was a general term for
 any callable attached to an instance or class, then you might have a point.
 But you're doing something much weirder: you are arguing that given two
 objects which are *identical* save for their object ID, one might be called
 a method, and the other not, due solely to where it was created. Not even
 where it was retrieved from, but where it was created.

 If you believe that method or not depends on where the function was
 defined, then this will really freak you out:


 py class Q:
 ... def f(self): pass  # f defined inside the class
 ...
 py def f(self): pass  # f defined outside the class
 ...
 py f, Q.f = Q.f, f  # Swap the inside f and the outside f.
 py instance = Q()
 py instance.f  # Uses outside f, so not a real method!
 bound method Q.f of __main__.Q object at 0xb7b8fcec
 py MethodType(f, instance)  # Uses inside f, so is a real method!
 bound method Q.f of __main__.Q object at 0xb7b8fcec

You are really missing the point, if you think that surprises me.

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


Re: dunder-docs (was Python is DOOMED! Again!)

2015-02-03 Thread Chris Angelico
On Tue, Feb 3, 2015 at 9:38 PM, Marko Rauhamaa ma...@pacujo.net wrote:
 It's slightly sad that Python exposes the two-level attribute lookup. It
 would be more elegant if, conceptually, all methods were retrieved from
 the object's attribute dict. That way, the class would be simply a
 mundane optimization trick instead of a metaphysical entity.


That's the ECMAScript model of classes - prototype-based. It's not
Python's. There are many different ways to do things.

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


Re: dunder-docs (was Python is DOOMED! Again!)

2015-02-03 Thread Marko Rauhamaa
Devin Jeanpierre jeanpierr...@gmail.com:

 It isn't mystical. There are differences in semantics of defining
 methods inside or outside of a class that apply in certain situations
 (e.g. super(), metaclasses). You have cherrypicked an example that
 avoids them.

It's slightly sad that Python exposes the two-level attribute lookup. It
would be more elegant if, conceptually, all methods were retrieved from
the object's attribute dict. That way, the class would be simply a
mundane optimization trick instead of a metaphysical entity.

   A class instance has a namespace implemented as a dictionary which is
   the first place in which attribute references are searched. When an
   attribute is not found there, and the instance’s class has an
   attribute by that name, the search continues with the class
   attributes.
   [URL: https://docs.python.org/3/reference/datamodel.html]


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


Re: dunder-docs (was Python is DOOMED! Again!)

2015-02-03 Thread Ethan Furman
On 02/03/2015 03:32 PM, Marko Rauhamaa wrote:
 Gregory Ewing greg.ew...@canterbury.ac.nz:
 
 You seem to be suggesting an optimisation that pre-creates bound
 methods when the instance is created. Keeping a cache of bound methods
 would achieve the same thing without changing the semantics. I think
 CPython might already be doing that, but I'm not sure.
 
 No, I'm saying Python should behave differently.
 
 Python:
 
 class A:
...def f(self):
...   print(f)
...def g(self):
...   print(g)
... 
 a = A()
 a.__class__.f = a.__class__.g
 a.f()
g
 
 In my preferred semantics, a.f() would print
 
 a.f()
f

That is, as you acknowledge, not Python.

Thankfully, it will also never be Python.

However, because Python is so awesome, you can twist your own code to behave 
that way, to a point: simply have your
__init__ ( or __new__) populate the instance dict with all non-dunder methods.

Or even better, implement your own proto(mumble) type stack in Python, so there 
is some warning that your instances
don't behave quite like normal Python instances.

--
~Ethan~



signature.asc
Description: OpenPGP digital signature
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: dunder-docs (was Python is DOOMED! Again!)

2015-02-03 Thread Devin Jeanpierre
On Mon, Feb 2, 2015 at 6:20 AM, Steven D'Aprano
steve+comp.lang.pyt...@pearwood.info wrote:
 Devin Jeanpierre wrote:
 Oops, I just realized why such a claim might be made: the
 documentation probably wants to be able to say that any method can use
 super(). So that's why it claims that it isn't a method unless it's
 defined inside a class body.

 You can use super anywhere, including outside of classes. The only thing you
 can't do is use the Python 3 super hack which automatically fills in the
 arguments to super if you don't supply them. That is compiler magic which
 truly does require the function to be defined inside a class body. But you
 can use super outside of classes:

Obviously, I was referring to no-arg super.

Please assume good faith and non-ignorance on my part.

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


Re: dunder-docs (was Python is DOOMED! Again!)

2015-02-03 Thread Gregory Ewing

Marko Rauhamaa wrote:

For (almost) all practical purposes, that is the Python way as well. If
object instantiation (conceptually) copied the class's methods into the
object's dict, you'd get the semantics I'm looking for.


If things worked the way you want, it would be
impossible to store a function in an instance
attribute and get it out again *without* it
being treated as a method and getting 'self'
added to its arguments. That would be a
considerable nuisance when dealing with
callbacks and the like.

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


Re: dunder-docs (was Python is DOOMED! Again!)

2015-02-03 Thread Gregory Ewing

Steven D'Aprano wrote:

If some methods can be defined outside of the body of a class, then being
defined inside the body of a class does not define a method.


Nobody's disputing that. The business about super() is
just a possible reason for the glossary to define the
word 'method' in a more restricted way -- because it
simplifies the wording of *other* parts of the docs
that talk about super().

Another thing to consider is that while tricks like
manually inserting __class__ into a function may work
today with CPython, they might not work in future versions
or other implementations. So there are good reasons for
the docs to be conservative about what they promise.

Also, with Python being so dynamic, just about *anything*
you can say about its usual behaviour can be circumvented
with enough hackery. If the docs were to pedantically
take all of those possibilities into account at every
turn, they would be so dense and impenetrable as to
be nearly useless to anyone other than language lawyers.

All this started when I pointed out that *if* you take
the glossary definition of the term 'method' at its
word, then what the docs say about the __dir__ method
won't lead you to think that attaching it to an instance
would work. That's true regardless of whether you think
the glossary definition is too restrictive or not.

I wouldn't have thought that this obvservation would
be so controversial. But maybe I'm wrong, and Python
really is doomed -- to death by language lawyering!-)

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


Re: dunder-docs (was Python is DOOMED! Again!)

2015-02-03 Thread Marko Rauhamaa
Gregory Ewing greg.ew...@canterbury.ac.nz:

 Marko Rauhamaa wrote:
 For (almost) all practical purposes, that is the Python way as well. If
 object instantiation (conceptually) copied the class's methods into the
 object's dict, you'd get the semantics I'm looking for.

 If things worked the way you want, it would be impossible to store a
 function in an instance attribute and get it out again *without* it
 being treated as a method and getting 'self' added to its arguments.
 That would be a considerable nuisance when dealing with callbacks and
 the like.

Sorry, you'll have to elaborate. I don't quite follow you.

Right now Python generates the trampoline from the class prototype every
time you call a method. If the semantics allowed, you could create the
trampoline at instantiation time (for better or worse). That way, the
problem you seem to be referring to wouldn't materialize.


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


Re: dunder-docs (was Python is DOOMED! Again!)

2015-02-02 Thread Steven D'Aprano
Devin Jeanpierre wrote:

 Oops, I just realized why such a claim might be made: the
 documentation probably wants to be able to say that any method can use
 super(). So that's why it claims that it isn't a method unless it's
 defined inside a class body.


You can use super anywhere, including outside of classes. The only thing you
can't do is use the Python 3 super hack which automatically fills in the
arguments to super if you don't supply them. That is compiler magic which
truly does require the function to be defined inside a class body. But you
can use super outside of classes:


py class A(list):
... pass
...
py x = super(A)  # Unbound super object!
py x.__get__(A).append
method 'append' of 'list' objects
py a = A()
py x.__get__(a).append
built-in method append of A object at 0xb7b8beb4



-- 
Steven

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


Re: Python is DOOMED! Again!

2015-02-02 Thread Rustom Mody
On Monday, February 2, 2015 at 1:13:30 PM UTC+5:30, Paul Rubin wrote:
 Steven D'Aprano writes:
  No apples and no oranges aren't the same thing, but if somebody is 
  expecting 
  no apples, and I give them no oranges instead, it would be churlish for 
  them 
  to complain that none of them are the wrong kind of fruit.
 
 https://davedevine.wordpress.com/2011/01/20/the-sartre-joke/

Actually the Sartre joke is more applicable to haskell than it might appear at 
first blush.

li = [1,2,3] : [Int] -- a monomorphic type
just as
lc = ['a','b','c'] : [Char]
lli = [[1,2],[3]] : [[Int]]

However [] is a polymorphic value ie
[] : [t]   -- t is a type variable

And now if we take
tail (tail (tail li)) 
you get []
just as if you take
tail (tail lli)

However the two '[]-s' are of different types
and so if you try to say append them you will get a Sartre error:

The list of no integers is incompatible with the list of no lists of integers
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: dunder-docs (was Python is DOOMED! Again!)

2015-02-02 Thread Devin Jeanpierre
On Mon, Feb 2, 2015 at 4:06 AM, Steven D'Aprano
steve+comp.lang.pyt...@pearwood.info wrote:
 On Sun, Feb 1, 2015 at 11:15 PM, Steven D'Aprano
 steve+comp.lang.pyt...@pearwood.info wrote:
 Both K.f and K.g are methods, even though only one meets the definition
 given in the glossary. The glossary is wrong.

I agree, it oversimplified and has made a useless distinction here.

 Even if it is so defined, the definition is wrong. You can define methods
 on an instance. I showed an example of an instance with its own personal
 __dir__ method, and showed that dir() ignores it if the instance belongs
 to a new-style class but uses it if it is an old-style class.

 You didn't define a method, you defined a callable attribute.

 That is wrong. I defined a method:

 py from types import MethodType
 py type(instance.f) is MethodType
 True


 instance.f is a method by the glossary definition. Its type is identical to
 types.MethodType, which is what I used to create a method by hand.

You are assuming that they are both methods, just because they are
instances of a type called MethodType. This is like assuming that a
Tree() object is made out of wood.

The documentation is free to define things in terms other than types
and be correct. There are many properties of functions-on-classes that
callable instance attributes that are instances of MethodType do not
have, as we've already noticed. isinstance can say one thing, and the
documentation another, and both can be right, because they are saying
different things.


For an example we can all agree on, this is not an instance of
collections.Iterable, but the docs claim it is iterable:
https://docs.python.org/2/glossary.html#term-iterable

class MyIterable(object):
def __getitem__(self, i): return i

The docs are not wrong, they are just making a distinction for
humans that is separate from the python types involved. This is OK.

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


Re: dunder-docs (was Python is DOOMED! Again!)

2015-02-02 Thread Steven D'Aprano
Rustom Mody wrote:

 My point was more methodological/sociological than technical:
 
 Are these dunder methods as 'internal' as say the register-allocation used
 by a C compiler?


Dunder methods are implementation, not interface. If you are the class
author, then you care about the implementation, and write dunder methods.
But as the class user, you should not call dunder methods directly, instead
always go through the public interface:

# Not these.
a.__dir__()
seq.__len__()
x.__add__(y)
spam.__eq__(ham)

# Use these
dir(a)
len(seq)
x + y
spam == ham



-- 
Steven

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


Re: dunder-docs (was Python is DOOMED! Again!)

2015-02-02 Thread Devin Jeanpierre
On Mon, Feb 2, 2015 at 5:00 AM, Devin Jeanpierre jeanpierr...@gmail.com wrote:
 On Mon, Feb 2, 2015 at 4:06 AM, Steven D'Aprano
 steve+comp.lang.pyt...@pearwood.info wrote:
 On Sun, Feb 1, 2015 at 11:15 PM, Steven D'Aprano
 steve+comp.lang.pyt...@pearwood.info wrote:
 Both K.f and K.g are methods, even though only one meets the definition
 given in the glossary. The glossary is wrong.

 I agree, it oversimplified and has made a useless distinction here.

Oops, I just realized why such a claim might be made: the
documentation probably wants to be able to say that any method can use
super(). So that's why it claims that it isn't a method unless it's
defined inside a class body.

-- Devin

 Even if it is so defined, the definition is wrong. You can define methods
 on an instance. I showed an example of an instance with its own personal
 __dir__ method, and showed that dir() ignores it if the instance belongs
 to a new-style class but uses it if it is an old-style class.

 You didn't define a method, you defined a callable attribute.

 That is wrong. I defined a method:

 py from types import MethodType
 py type(instance.f) is MethodType
 True


 instance.f is a method by the glossary definition. Its type is identical to
 types.MethodType, which is what I used to create a method by hand.

 You are assuming that they are both methods, just because they are
 instances of a type called MethodType. This is like assuming that a
 Tree() object is made out of wood.

 The documentation is free to define things in terms other than types
 and be correct. There are many properties of functions-on-classes that
 callable instance attributes that are instances of MethodType do not
 have, as we've already noticed. isinstance can say one thing, and the
 documentation another, and both can be right, because they are saying
 different things.


 For an example we can all agree on, this is not an instance of
 collections.Iterable, but the docs claim it is iterable:
 https://docs.python.org/2/glossary.html#term-iterable

 class MyIterable(object):
 def __getitem__(self, i): return i

 The docs are not wrong, they are just making a distinction for
 humans that is separate from the python types involved. This is OK.

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


Re: dunder-docs (was Python is DOOMED! Again!)

2015-02-02 Thread Steven D'Aprano
Devin Jeanpierre wrote:

 On Mon, Feb 2, 2015 at 4:06 AM, Steven D'Aprano
 steve+comp.lang.pyt...@pearwood.info wrote:

 instance.f is a method by the glossary definition. Its type is identical
 to types.MethodType, which is what I used to create a method by hand.
 
 You are assuming that they are both methods, just because they are
 instances of a type called MethodType. This is like assuming that a
 Tree() object is made out of wood.

No. It is assuming that a Tree() object is a Tree() object.

Run this code:

# === cut ===

class K(object):
def f(self): pass

def f(self): pass

instance = K()
things = [instance.f, f.__get__(instance, K)]
from random import shuffle
shuffle(things)
print(things)

# === cut ===


You allege that one of these things is a method, and the other is not. I
challenge you to find any behavioural or functional difference between the
two. (Object IDs don't count.)

If you can find any meaningful difference between the two, I will accept
that methods have to be created as functions inside a class body.

Otherwise you are reduced to claiming that there is some sort of mystical,
undetectable essence or spirit that makes one of those two objects a
real method and the other one a fake method, even though they have the same
type, the same behaviour, and there is no test that can tell you which is
which.


 The documentation is free to define things in terms other than types
 and be correct. 

If you wanted to argue that method is a generic term, and we have instance
methods, class methods, static methods, and any other sort of method we
care to create using the descriptor protocol, then I would agree you have a
point.

But since we're just talking about instance methods, Python doesn't care how
they came into existence. You can use def to create a function inside a
class body, inject a function into the class, call the descriptor __get__
method, or use the types.MethodType type object, it is all the same. You
can use a def statement, or a lambda, or types.FunctionType if you are
really keen. It makes no difference.

Do I expect the glossary to go into such pedantic detail? No, of course not.
But I do expect anyone with a few years of Python programming experience to
be able to understand that what makes a method be a method is its type and
behaviour, not where it came from.


 There are many properties of functions-on-classes that 
 callable instance attributes that are instances of MethodType do not
 have, as we've already noticed. isinstance can say one thing, and the
 documentation another, and both can be right, because they are saying
 different things.
 
 
 For an example we can all agree on, this is not an instance of
 collections.Iterable, but the docs claim it is iterable:
 https://docs.python.org/2/glossary.html#term-iterable
 
 class MyIterable(object):
 def __getitem__(self, i): return i

Iterable is a generic term, not a type. Despite the existence of the
collections.Iterable ABC, iterable refers to any type which can be
iterated over, using either of two different protocols.

As I said above, if you wanted to argue that method was a general term for
any callable attached to an instance or class, then you might have a point.
But you're doing something much weirder: you are arguing that given two
objects which are *identical* save for their object ID, one might be called
a method, and the other not, due solely to where it was created. Not even
where it was retrieved from, but where it was created.

If you believe that method or not depends on where the function was
defined, then this will really freak you out:


py class Q:
... def f(self): pass  # f defined inside the class
...
py def f(self): pass  # f defined outside the class
...
py f, Q.f = Q.f, f  # Swap the inside f and the outside f.
py instance = Q()
py instance.f  # Uses outside f, so not a real method!
bound method Q.f of __main__.Q object at 0xb7b8fcec
py MethodType(f, instance)  # Uses inside f, so is a real method!
bound method Q.f of __main__.Q object at 0xb7b8fcec



-- 
Steven

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


Re: dunder-docs (was Python is DOOMED! Again!)

2015-02-02 Thread Rustom Mody
On Monday, February 2, 2015 at 10:57:27 AM UTC+5:30, Vito De Tullio wrote:
 Steven D'Aprano wrote:
 
  Checking the REPL first would have revealed that [].__dir__ raises
  AttributeError. In other words, lists don't have a __dir__ method.
 
 ?
 
 Python 3.4.2 (default, Nov 29 2014, 00:45:45) 
 [GCC 4.8.3] on linux
 Type help, copyright, credits or license for more information.
  [].__dir__()
 ['sort', '__contains__', '__init__', '__ge__', 'count', '__class__', 
 '__format__', '__mul__', 'index', '__rmul__', '__hash__', '__iter__', 
 'clear', '__subclasshook__', '__getitem__', 'reverse', 'append', '__ne__', 
 'pop', '__reduce__', '__add__', 'extend', '__gt__', '__sizeof__', 
 '__setattr__', '__imul__', '__dir__', '__le__', 'insert', '__repr__', 
 '__str__', '__getattribute__', '__len__', '__lt__', 'remove', '__new__', 
 '__reduce_ex__', 'copy', '__reversed__', '__delattr__', '__eq__', 
 '__setitem__', '__iadd__', '__doc__', '__delitem__']
  

Sure
But as I said (and probably Steven checked):

$ python
Python 2.7.8 (default, Oct 20 2014, 15:05:19) 
[GCC 4.9.1] on linux2
Type help, copyright, credits or license for more information.
 [].__dir__
Traceback (most recent call last):
  File stdin, line 1, in module
AttributeError: 'list' object has no attribute '__dir__'

---
My point was more methodological/sociological than technical:

Are these dunder methods as 'internal' as say the register-allocation used
by a C compiler?

In which case the language implementer is entitled to tell the vanilla 
programmer: Dunder methods (and their changingness) is none of your business

If however they are more on the public façade of the language then some better
docs would be nice
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: dunder-docs (was Python is DOOMED! Again!)

2015-02-02 Thread Steven D'Aprano
Steven D'Aprano wrote:

 Both K.f and K.g are methods, even though only one meets the definition
 given in the glossary. The glossary is wrong.

Oh I'm sure somebody is going to pick me up on this... 

In Python 2, they are methods. In Python 3, they are functions, and aren't
converted into methods until you access them via the instance:

K.f returns the function f

instance.f typically retrieves the function f from K, and converts it to a
method object bound to instance



-- 
Steven

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


Re: dunder-docs (was Python is DOOMED! Again!)

2015-02-02 Thread Steven D'Aprano
Devin Jeanpierre wrote:

 -- Devin
 
 On Sun, Feb 1, 2015 at 11:15 PM, Steven D'Aprano
 steve+comp.lang.pyt...@pearwood.info wrote:
 Gregory Ewing wrote:

 Steven D'Aprano wrote:
 [quote]
 If the object has a method named __dir__(), this method will
 be called and must return the list of attributes.
 [end quote]

 The first inaccuracy is that like all (nearly all?) dunder methods,
 Python only looks for __dir__ on the class, not the instance itself.

 It says method, not attribute, so technically
 it's correct. The methods of an object are defined
 by what's in its class.

 Citation please. I'd like to see where that is defined.
 
 https://docs.python.org/3/glossary.html#term-method

Run this code using any version of Python from 1.5 onwards, and you will see
that the definition is wrong:


# === cut ===

class K:
def f(self): pass

# Define a function OUTSIDE of a class body.
def g(self): pass

K.g = g
instance = K()
assert type(instance.f) is type(instance.g)
print(type(instance.f))
print(type(instance.g))

# === cut ===


Both K.f and K.g are methods, even though only one meets the definition
given in the glossary. The glossary is wrong.

Or rather, it is not so much that it is *wrong*, but that it is incomplete
and over-simplified. It describes how methods are normally (but not always)
defined, but not what they are. It is rather like defining coffee as the
milky drink you buy from Starbucks, then insisting that the black liquid
that you drank in an Italian restaurant cannot be coffee because you didn't
buy it from Starbucks.

Glossary entries are typically simplified, not exhaustive. It is not wise to
take a three line glossary entry as a complete, definite explanation. In
this case the glossary fails to tell you that methods are not *required* to
be defined inside a class body, that is merely the usual way to do it.



 Even if it is so defined, the definition is wrong. You can define methods
 on an instance. I showed an example of an instance with its own personal
 __dir__ method, and showed that dir() ignores it if the instance belongs
 to a new-style class but uses it if it is an old-style class.
 
 You didn't define a method, you defined a callable attribute.

That is wrong. I defined a method:

py from types import MethodType
py type(instance.f) is MethodType
True


instance.f is a method by the glossary definition. Its type is identical to
types.MethodType, which is what I used to create a method by hand.

I could also call the descriptor __get__ method by hand, if you prefer:

py def h(self): pass
...
py method = h.__get__(K, instance)
py assert type(method) is type(instance.f)
py print(method)
bound method type.h of class '__main__.K'



-- 
Steven

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


Re: dunder-docs (was Python is DOOMED! Again!)

2015-02-01 Thread Ethan Furman
On 01/31/2015 09:36 PM, Rustom Mody wrote:
 
 And a student asked me the diff between
 dir([])
 and
 [].__dir__()
 
 I didnt know what to say...
 Now surely the amount of python I dont know is significantly larger than what 
 I know
 Still it would be nice to have surface-syntax ←→ dunder-magic more
 systematically documented

I don't have a complete answer for you, but I can say this:

In simple cases (such as __len__) there is little difference between calling 
the surface operator and the dunder version
(the error message differs in this case).

In more complex cases (such as __add__) using the surface syntax (+) buys lots 
of extras:

  - if one operand is a subclass of the other, calling its __add__ or __radd__ 
method as appropriate
  - if the first operand returns NotImplemented, calling the other operand's 
__radd__ to see if that works
  - possibly others that I don't recall at the moment

Basically, unless you're programming at the system (or class internals) level, 
don't call dunder methods directly.

--
~Ethan~



signature.asc
Description: OpenPGP digital signature
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: dunder-docs (was Python is DOOMED! Again!)

2015-02-01 Thread Steven D'Aprano
Rustom Mody wrote:

 The other day I was taking a class in which I was showing
 - introspection for discovering -- help, type, dir etc at the repl
 - mapping of surface syntax to internals eg. a + b ←→ a.__add__(b)
 
 And a student asked me the diff between
 dir([])
 and
 [].__dir__()
 
 I didnt know what to say...

Surely the right answer would have been I don't know, let's check the
interactive interpreter first, and the docs second.

Checking the REPL first would have revealed that [].__dir__ raises
AttributeError. In other words, lists don't have a __dir__ method.

Checking the docs would then have revealed that __dir__ is only required
when a class wishes to customise the output of dir(). Or at least, that's
what I recall them saying. I'm too lazy to look it up :-)

Oh very well, because it's you...

https://docs.python.org/2/library/functions.html#dir

Note that there are two inaccuracies in the documentation for __dir__:

[quote]
If the object has a method named __dir__(), this method will 
be called and must return the list of attributes.
[end quote]

The first inaccuracy is that like all (nearly all?) dunder methods, Python
only looks for __dir__ on the class, not the instance itself. Hence:


py class K(object):
... def __dir__(self):
... return [eggs, spam]
...
py k = K()
py from types import MethodType
py k.__dir__ = MethodType(lambda self: [cheese, spam], k)
py dir(k)
['eggs', 'spam']

Well, actually that's only true for new-style classes. For old-style classic
classes (Python 2 only), it will work on the instance as well. That is, in
Python 2 only, if I had left out the object base class, dir(k) would have
returned [cheese, spam].

Now that I have confused your students, we shall never mention classic
classes again (until next time).

The second inaccuracy is that method should not return the attributes
themselves, but their names.


 Now surely the amount of python I dont know is significantly larger than
 what I know Still it would be nice to have surface-syntax ←→ dunder-magic
 more systematically documented

Each function or operator is unique. However, there are certain general
behaviours that typically hold.

Dunder methods are only looked up on the class, not the instance. (Except
for classic classes, if there are any exceptions to this rule, it is
probably a bug.) So the correct analog of surface syntax `obj + spam` is
*not* `obj.__add__(spam)` but actually:

type(obj).__add__(spam)

Similar for other operators and functions. In the case of dir, the
comparison should be:

dir([]) versus type([]).__dir__()

Except of course dir doesn't require there to be a __dir__ method.

len tries to call __len__ if it exists, and if not, it tries walking the
iterable counting items.

bool tries calling __bool__ (Python 3) or __nonzero__ (Python 2), if it
exists. If not, it tries returning len(obj) != 0. If that fails, objects
are true by default.

In the case of operators, operator syntax `x $ y` for some operator $ will
generally do something like this:


X = type(x)
Y = type(y)
if issubclass(Y, X):
# Try the reflected version first, if it exists.
dunder = getattr(Y, __rdunder__, None)
if dunder:
result = dunder(y, x)
if result is not NotImplemented:
return result
# Or the non-reflected version second.
dunder = getattr(X, __dunder__, None)
if dunder:
result = dunder(x, y)
if result is not NotImplemented:
return result
else:
 # Like the above, except we check the non-reflected 
 # version first, and the reflected version second.
 ...
raise TypeError


Some methods are their own reflection, e.g. __eq__. In the special case of
__eq__ and __ne__, if the class defines one method but not the other,
recent versions of Python will automatically call the other method and
return its boolean not. Unary operators obviously have no concept of a
reflected version.

Comparison operators prefer to call the rich comparison methods but may fall
back on the older __cmp__ dunder method (Python 2 only).


-- 
Steven

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


Re: Python is DOOMED! Again!

2015-02-01 Thread Steven D'Aprano
Devin Jeanpierre wrote:

 It's really only dynamically typed languages that have a single null
 value of a single type. Maybe I misunderstand the original statement.

Pascal is statically typed and has a single null pointer compatible with all
pointer types. C has a single nil pointer compatible with all pointer
types. I expect that the Modula and Oberon family of languages copied
Pascal, which probably copied Algol.



-- 
Steven

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


Re: dunder-docs (was Python is DOOMED! Again!)

2015-02-01 Thread Steven D'Aprano
Steven D'Aprano wrote:

 len tries to call __len__ if it exists, and if not, it tries walking the
 iterable counting items.

Hmmm, I may have mis-remembered that. Perhaps I'm thinking of Ruby.



-- 
Steven

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


Re: Python is DOOMED! Again!

2015-02-01 Thread Steven D'Aprano
Steven D'Aprano wrote:

 Devin Jeanpierre wrote:
 
 It's really only dynamically typed languages that have a single null
 value of a single type. Maybe I misunderstand the original statement.
 
 Pascal is statically typed and has a single null pointer compatible with
 all pointer types. 

I'm not having a good night... 

In Pascal, it is spelled *nil*, not null, and it is a keyword.


 C has a single nil pointer compatible with all pointer 
 types.

And in C it is null, which apparently isn't an actual value but a macro.


 I expect that the Modula and Oberon family of languages copied 
 Pascal, which probably copied Algol.

At least I was right about Modula:


The constant NIL is compatible with all pointer types, and designates a
pointer that does not point to any object.  NIL can be assigned to any
pointer type, and any pointer type can be compared to NIL.

http://www.modula2.org/reference/pointertypes.php

Likewise for Oberon and Oberon-2.



-- 
Steven

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


Re: Python is DOOMED! Again!

2015-02-01 Thread Devin Jeanpierre
On Sun, Feb 1, 2015 at 8:31 AM, Steven D'Aprano
steve+comp.lang.pyt...@pearwood.info wrote:
 Paul Rubin wrote:
 It's completely practical: polymorphism and type inference get you the
 value you want with usually no effort on your part.

 But it's the usually that bites you.

 If I have an arbitrary pointer, and I want to check if it is safe to
 dereference, how do I do it? Surely I'm not expected to write something
 like:

 if type(ptr) == A:
 if ptr != Anil: ...
 if type(ptr) == B:
 if ptr != Bnil: ...

 etc. That would be insane. So how does Haskell do this?

Haskell has different nulls in the same sense Java does: there's one
keyword, whose type varies by context. Unlike Java, there is no way at
all to cast different nulls to different types.  Haskell has return
value polymorphism and generics, so it's very easy for a function to
return values of different types depending on type parameters. So this
isn't even compiler hackery, it's ordinary.

Also, you don't dereference in Haskell, you unpack. Python and Haskell code:

if x is None:
print(Not found!)
else:
print x

case x of
Nothing - putStrLn Not found
Some y - putStrLn (show y)

Both of these work whenever x is something that can be null and can be
shown -- in Haskell, that's anything of type Maybe T, where you have
access to a Show implementation for T.  In Python, None is its own
type/value, in Haskell there is an incompatible Nothing for each T.

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


Re: Python is DOOMED! Again!

2015-02-01 Thread Steven D'Aprano
Paul Rubin wrote:

 Steven D'Aprano steve+comp.lang.pyt...@pearwood.info writes:
 Some degree of weakness in a type system is not necessarily bad. Even the
 strongest of languages usually allow a few exceptions, such as numeric
 coercions.
 
 Haskell doesn't have automatic coercions of any sort.  You have to call
 a conversion function if you want to turn an Int into an Integer.

That's unusual but not unheard of.


 I've never come across a language that has pointers which insists on
 having a separate Nil pointer for ever pointer type
 
 Haskell's idiomatic substitute for a null pointer is a Nothing value
 (like Python's None) and there's a separate one for every type.  The FFI
 offers actual pointers (Foreign.Ptr) and there is a separate nullPtr
 for every type.

Well, I live and learn.


 the compiler will allow Nil to be used for any pointer type. Anything
 else would be impractical.
 
 It's completely practical: polymorphism and type inference get you the
 value you want with usually no effort on your part.

But it's the usually that bites you.

If I have an arbitrary pointer, and I want to check if it is safe to
dereference, how do I do it? Surely I'm not expected to write something
like:

if type(ptr) == A:
if ptr != Anil: ...
if type(ptr) == B:
if ptr != Bnil: ... 

etc. That would be insane. So how does Haskell do this?

 
 What if you add two empty objects?
 js {} + {}
 
 OMG, javascript is worse than I thought
 
 https://www.destroyallsoftware.com/talks/wat
 
 Can't view, needs flash. :(

At the bottom of the page is a link to a .mov version. If you can't
play .mov files either, contact me off list.


 Try this instead (NFSW): https://www.youtube.com/watch?v=FJ7QsEytQq4



-- 
Steven

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


Re: Python is DOOMED! Again!

2015-02-01 Thread Devin Jeanpierre
On Sun, Feb 1, 2015 at 8:34 AM, Steven D'Aprano
steve+comp.lang.pyt...@pearwood.info wrote:
 Devin Jeanpierre wrote:

 It's really only dynamically typed languages that have a single null
 value of a single type. Maybe I misunderstand the original statement.

 Pascal is statically typed and has a single null pointer compatible with all
 pointer types. C has a single nil pointer compatible with all pointer
 types. I expect that the Modula and Oberon family of languages copied
 Pascal, which probably copied Algol.

No, C has a NULL macro which evaluates to something which coerces to
any pointer type and will be the null value of that type. But there's
one null value per type. The C standard makes no guarantees that they
are compatible in any way, e.g. they can be of different sizes. On
some systems, the null function pointer will have a size of N, where
the null int pointer will have a size of M, where N != M -- so these
are clearly not the same null value.

I don't know Pascal, but I wouldn't be surprised if something similar
held, as nonuniform pointer sizes were a thing once.

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


Re: dunder-docs (was Python is DOOMED! Again!)

2015-02-01 Thread Ian Kelly
On Sun, Feb 1, 2015 at 9:55 AM, Steven D'Aprano
steve+comp.lang.pyt...@pearwood.info wrote:
 Steven D'Aprano wrote:

 len tries to call __len__ if it exists, and if not, it tries walking the
 iterable counting items.

 Hmmm, I may have mis-remembered that. Perhaps I'm thinking of Ruby.

I think you just got it backward. iter will call __iter__ if it
exists, and will try to fall back on __len__ and __getitem__ to
iterate otherwise.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Python is DOOMED! Again!

2015-02-01 Thread Devin Jeanpierre
On Sun, Feb 1, 2015 at 2:27 PM, Paul Rubin no.email@nospam.invalid wrote:
 Devin Jeanpierre jeanpierr...@gmail.com writes:
 That said, Haskell (and the rest) do have a sort of type coercion, of
 literals at compile time (e.g. 3 can be an Integer or a Double
 depending on how you use it.)

 That's polymorphism, not coercion.

OK, yes, that fits better into how Haskell works. After all, that's
how Nothing works. If 3 is just a (magic) constructor, then it's no different.

 The compiler figures out at compile
 time what type of 3 you actually mean: there is never an automatic
 runtime conversion.  sqrt(3) works because sqrt expects a floating
 argument so the compiler deduces that the 3 that you wrote denotes a
 float.  sqrt(3+length(xs)) has to fail because length returns an int, so
 3+length(xs) is an int, and you can't pass an int to sqrt.

 BTW it's weird that in this thread, and in the programmer community at
 large, int-string is considered worse than int-float

 Hehe, though int-string leads to plenty of weird bugs.

 Haskell's idiomatic substitute for a null pointer is a Nothing value
 For that matter, how is this (first part) different from, say, Java?

 In Java, functions expecting to receve sensible values can get null by
 surprise.  In Haskell, if a term can have a Nothing value, that has to
 be reflected in its type.  Haskell's bug-magnet counterpart to Java's
 null values is Bottom, an artifact of lazy evaluation.  E.g. you can
 write
x = 3 / 0
 someplace in your program, and the program will accept this and run
 merrily until you try to actually print something that depends on x,
 at which point it crashes.

This isn't a difference in whether there are multiple nulls, though.

I answered my own question later, by accident: Java nulls are castable
to each other if you do it explicitly (routing through Object -- e.g.
(Something)((Object) ((SomeOtherThing) null.

So in that sense, there is only one null, just with some arbitrary
compiler distinctions you can break through if you try hard enough.

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


Re: Python is DOOMED! Again!

2015-02-01 Thread Paul Rubin
Devin Jeanpierre jeanpierr...@gmail.com writes:
 That said, Haskell (and the rest) do have a sort of type coercion, of
 literals at compile time (e.g. 3 can be an Integer or a Double
 depending on how you use it.)

That's polymorphism, not coercion.  The compiler figures out at compile
time what type of 3 you actually mean: there is never an automatic
runtime conversion.  sqrt(3) works because sqrt expects a floating
argument so the compiler deduces that the 3 that you wrote denotes a
float.  sqrt(3+length(xs)) has to fail because length returns an int, so
3+length(xs) is an int, and you can't pass an int to sqrt.

 BTW it's weird that in this thread, and in the programmer community at
 large, int-string is considered worse than int-float

Hehe, though int-string leads to plenty of weird bugs.

 Haskell's idiomatic substitute for a null pointer is a Nothing value
 For that matter, how is this (first part) different from, say, Java?

In Java, functions expecting to receve sensible values can get null by
surprise.  In Haskell, if a term can have a Nothing value, that has to
be reflected in its type.  Haskell's bug-magnet counterpart to Java's
null values is Bottom, an artifact of lazy evaluation.  E.g. you can
write
   x = 3 / 0
someplace in your program, and the program will accept this and run
merrily until you try to actually print something that depends on x,
at which point it crashes.  
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: dunder-docs (was Python is DOOMED! Again!)

2015-02-01 Thread Emile van Sebille

On 2/1/2015 12:45 PM, Chris Angelico wrote:

Simple answer: You write dunder methods and the interpreter calls
them. You don't call them yourself.

I can't currently think of any situation where it's appropriate to
call a dunder method manually (cue the swamping of such situations on
the list); you just call dir() or the + operator or whatever it be.


calling the parent dunder method from within the overiding new method 
comes to mind.


Emile


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


Re: dunder-docs (was Python is DOOMED! Again!)

2015-02-01 Thread Chris Angelico
On Sun, Feb 1, 2015 at 4:36 PM, Rustom Mody rustompm...@gmail.com wrote:
 The other day I was taking a class in which I was showing
 - introspection for discovering -- help, type, dir etc at the repl
 - mapping of surface syntax to internals eg. a + b ←→ a.__add__(b)

 And a student asked me the diff between
 dir([])
 and
 [].__dir__()

 I didnt know what to say...

Simple answer: You write dunder methods and the interpreter calls
them. You don't call them yourself.

I can't currently think of any situation where it's appropriate to
call a dunder method manually (cue the swamping of such situations on
the list); you just call dir() or the + operator or whatever it be.

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


Re: Python is DOOMED! Again!

2015-02-01 Thread Paul Rubin
Steven D'Aprano steve+comp.lang.pyt...@pearwood.info writes:
 C has a single nil pointer compatible with all pointer types.

C++11 has a separate type just for the null pointer, which can be
automatically coerced to other pointer types.  I'm not sure but I think
that means it is couthing up slightly.

http://en.cppreference.com/w/cpp/types/nullptr_t
http://en.cppreference.com/w/cpp/language/nullptr
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: dunder-docs (was Python is DOOMED! Again!)

2015-02-01 Thread Rustom Mody
On Sunday, February 1, 2015 at 9:51:11 PM UTC+5:30, Steven D'Aprano wrote:
 Rustom Mody wrote:
 
  The other day I was taking a class in which I was showing
  - introspection for discovering -- help, type, dir etc at the repl
  - mapping of surface syntax to internals eg. a + b ←→ a.__add__(b)
  
  And a student asked me the diff between
  dir([])
  and
  [].__dir__()
  
  I didnt know what to say...
 
 Surely the right answer would have been I don't know, let's check the
 interactive interpreter first, and the docs second.
 
 Checking the REPL first would have revealed that [].__dir__ raises
 AttributeError. In other words, lists don't have a __dir__ method.

??

$ python3
Python 3.4.2 (default, Oct  8 2014, 13:08:17) 
[GCC 4.9.1] on linux
Type help, copyright, credits or license for more information.
 [].__dir__
built-in method __dir__ of list object at 0x7f5dd2209f48
 dir([])
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', 
'__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', 
'__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', 
'__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', 
'__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', 
'__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 
'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 
'remove', 'reverse', 'sort']
 [].__dir__()
['__rmul__', '__str__', '__gt__', 'remove', '__class__', '__getitem__', 
'__format__', '__ne__', '__sizeof__', '__contains__', '__reduce__', '__add__', 
'sort', 'count', 'extend', '__mul__', '__imul__', '__reduce_ex__', 
'__setitem__', '__doc__', '__ge__', 'copy', '__init__', '__iadd__', '__hash__', 
'__delitem__', 'insert', '__iter__', '__repr__', '__le__', '__setattr__', 
'reverse', '__new__', '__eq__', '__len__', 'index', '__lt__', 'clear', 
'__subclasshook__', 'append', '__dir__', '__reversed__', '__getattribute__', 
'pop', '__delattr__']
 

What you are describing seems to be true for python2 though
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: dunder-docs (was Python is DOOMED! Again!)

2015-02-01 Thread Rustom Mody
On Monday, February 2, 2015 at 9:22:51 AM UTC+5:30, Rustom Mody wrote:
 On Sunday, February 1, 2015 at 9:51:11 PM UTC+5:30, Steven D'Aprano wrote:
  Rustom Mody wrote:
  
   The other day I was taking a class in which I was showing
   - introspection for discovering -- help, type, dir etc at the repl
   - mapping of surface syntax to internals eg. a + b ←→ a.__add__(b)
   
   And a student asked me the diff between
   dir([])
   and
   [].__dir__()
   
   I didnt know what to say...
  
  Surely the right answer would have been I don't know, let's check the
  interactive interpreter first, and the docs second.
  
  Checking the REPL first would have revealed that [].__dir__ raises
  AttributeError. In other words, lists don't have a __dir__ method.
 
 ??
 
 $ python3
 Python 3.4.2 (default, Oct  8 2014, 13:08:17) 
 [GCC 4.9.1] on linux
 Type help, copyright, credits or license for more information.
  [].__dir__
 built-in method __dir__ of list object at 0x7f5dd2209f48
  dir([])
 ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', 
 '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', 
 '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', 
 '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', 
 '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', 
 '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 
 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 
 'remove', 'reverse', 'sort']
  [].__dir__()
 ['__rmul__', '__str__', '__gt__', 'remove', '__class__', '__getitem__', 
 '__format__', '__ne__', '__sizeof__', '__contains__', '__reduce__', 
 '__add__', 'sort', 'count', 'extend', '__mul__', '__imul__', '__reduce_ex__', 
 '__setitem__', '__doc__', '__ge__', 'copy', '__init__', '__iadd__', 
 '__hash__', '__delitem__', 'insert', '__iter__', '__repr__', '__le__', 
 '__setattr__', 'reverse', '__new__', '__eq__', '__len__', 'index', '__lt__', 
 'clear', '__subclasshook__', 'append', '__dir__', '__reversed__', 
 '__getattribute__', 'pop', '__delattr__']
  
 
 What you are describing seems to be true for python2 though

And since they *looked* different I believed they were different.

Evidently not…

 s1= set([].__dir__())
 s2=set(dir([]))
 s1
{'__rmul__', '__str__', '__class__', '__ne__', '__repr__', '__format__', 
'__sizeof__', '__contains__', '__add__', 'sort', 'count', 'extend', 'remove', 
'__mul__', '__reduce__', '__imul__', '__reduce_ex__', '__setitem__', 'insert', 
'__doc__', '__ge__', 'index', 'copy', '__subclasshook__', '__getitem__', 
'__init__', '__iadd__', '__hash__', '__delitem__', '__iter__', '__le__', 
'__setattr__', 'reverse', '__new__', '__eq__', '__len__', '__lt__', 'clear', 
'__gt__', 'append', '__dir__', '__reversed__', '__getattribute__', 'pop', 
'__delattr__'}
 s2
{'__rmul__', '__str__', '__class__', '__ne__', '__repr__', '__format__', 
'__sizeof__', '__contains__', '__add__', 'sort', 'count', 'extend', '__mul__', 
'remove', '__imul__', '__reduce__', '__reduce_ex__', '__setitem__', 'insert', 
'__doc__', '__ge__', '__subclasshook__', 'copy', 'index', '__getitem__', 
'__iadd__', '__init__', '__hash__', '__delitem__', '__iter__', '__le__', 
'__setattr__', 'reverse', '__new__', '__eq__', '__len__', '__lt__', 'clear', 
'__gt__', 'append', '__dir__', '__reversed__', '__getattribute__', 'pop', 
'__delattr__'}
 len(s1)
45
 len(s2)
45
 s1 - s2
set()
 s2 - s1
set()
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Python is DOOMED! Again!

2015-02-01 Thread Steven D'Aprano
Devin Jeanpierre wrote:

 So in that sense, there is only one null, just with some arbitrary
 compiler distinctions you can break through if you try hard enough.

Woo hoo! I was right!

*Dances the Dance of Victory!*



-- 
Steven

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


Re: dunder-docs (was Python is DOOMED! Again!)

2015-02-01 Thread Rustom Mody
On Monday, February 2, 2015 at 9:34:53 AM UTC+5:30, Rustom Mody wrote:
 On Monday, February 2, 2015 at 9:22:51 AM UTC+5:30, Rustom Mody wrote:
  On Sunday, February 1, 2015 at 9:51:11 PM UTC+5:30, Steven D'Aprano wrote:
   Rustom Mody wrote:
   
The other day I was taking a class in which I was showing
- introspection for discovering -- help, type, dir etc at the repl
- mapping of surface syntax to internals eg. a + b ←→ a.__add__(b)

And a student asked me the diff between
dir([])
and
[].__dir__()

I didnt know what to say...
   
   Surely the right answer would have been I don't know, let's check the
   interactive interpreter first, and the docs second.
   
   Checking the REPL first would have revealed that [].__dir__ raises
   AttributeError. In other words, lists don't have a __dir__ method.
  
  ??
  
  $ python3
  Python 3.4.2 (default, Oct  8 2014, 13:08:17) 
  [GCC 4.9.1] on linux
  Type help, copyright, credits or license for more information.
   [].__dir__
  built-in method __dir__ of list object at 0x7f5dd2209f48
   dir([])
  ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', 
  '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', 
  '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', 
  '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', 
  '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', 
  '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 
  'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 
  'remove', 'reverse', 'sort']
   [].__dir__()
  ['__rmul__', '__str__', '__gt__', 'remove', '__class__', '__getitem__', 
  '__format__', '__ne__', '__sizeof__', '__contains__', '__reduce__', 
  '__add__', 'sort', 'count', 'extend', '__mul__', '__imul__', 
  '__reduce_ex__', '__setitem__', '__doc__', '__ge__', 'copy', '__init__', 
  '__iadd__', '__hash__', '__delitem__', 'insert', '__iter__', '__repr__', 
  '__le__', '__setattr__', 'reverse', '__new__', '__eq__', '__len__', 
  'index', '__lt__', 'clear', '__subclasshook__', 'append', '__dir__', 
  '__reversed__', '__getattribute__', 'pop', '__delattr__']
   
  
  What you are describing seems to be true for python2 though
 
 And since they *looked* different I believed they were different.
 
 Evidently not…
 
  s1= set([].__dir__())
  s2=set(dir([]))
  s1
 {'__rmul__', '__str__', '__class__', '__ne__', '__repr__', '__format__', 
 '__sizeof__', '__contains__', '__add__', 'sort', 'count', 'extend', 'remove', 
 '__mul__', '__reduce__', '__imul__', '__reduce_ex__', '__setitem__', 
 'insert', '__doc__', '__ge__', 'index', 'copy', '__subclasshook__', 
 '__getitem__', '__init__', '__iadd__', '__hash__', '__delitem__', '__iter__', 
 '__le__', '__setattr__', 'reverse', '__new__', '__eq__', '__len__', '__lt__', 
 'clear', '__gt__', 'append', '__dir__', '__reversed__', '__getattribute__', 
 'pop', '__delattr__'}
  s2
 {'__rmul__', '__str__', '__class__', '__ne__', '__repr__', '__format__', 
 '__sizeof__', '__contains__', '__add__', 'sort', 'count', 'extend', 
 '__mul__', 'remove', '__imul__', '__reduce__', '__reduce_ex__', 
 '__setitem__', 'insert', '__doc__', '__ge__', '__subclasshook__', 'copy', 
 'index', '__getitem__', '__iadd__', '__init__', '__hash__', '__delitem__', 
 '__iter__', '__le__', '__setattr__', 'reverse', '__new__', '__eq__', 
 '__len__', '__lt__', 'clear', '__gt__', 'append', '__dir__', '__reversed__', 
 '__getattribute__', 'pop', '__delattr__'}
  len(s1)
 45
  len(s2)
 45
  s1 - s2
 set()
  s2 - s1
 set()

Well I continue to be fooled

 d1 = {k:getattr([],k) for k in [].__dir__()}

 d2 = {k:getattr([],k) for k in dir([])}
 d1 == d2
False

 len(d1)
45
 len(d2)
45

 d1.keys()
dict_keys(['__rmul__', '__str__', '__gt__', '__mul__', '__class__', '__ne__', 
'__format__', '__sizeof__', '__contains__', '__imul__', '__add__', 'sort', 
'count', 'extend', 'remove', '__init__', 'insert', '__setitem__', 'index', 
'__subclasshook__', 'copy', '__getitem__', '__iadd__', '__hash__', 
'__delitem__', '__reduce_ex__', '__iter__', '__repr__', '__le__', 
'__setattr__', 'reverse', '__new__', '__eq__', '__len__', '__doc__', '__lt__', 
'clear', '__ge__', 'append', '__dir__', '__reversed__', '__getattribute__', 
'pop', '__delattr__', '__reduce__'])
 d1.keys() == d2.keys()
True
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: dunder-docs (was Python is DOOMED! Again!)

2015-02-01 Thread Devin Jeanpierre
-- Devin

On Sun, Feb 1, 2015 at 11:15 PM, Steven D'Aprano
steve+comp.lang.pyt...@pearwood.info wrote:
 Gregory Ewing wrote:

 Steven D'Aprano wrote:
 [quote]
 If the object has a method named __dir__(), this method will
 be called and must return the list of attributes.
 [end quote]

 The first inaccuracy is that like all (nearly all?) dunder methods,
 Python only looks for __dir__ on the class, not the instance itself.

 It says method, not attribute, so technically
 it's correct. The methods of an object are defined
 by what's in its class.

 Citation please. I'd like to see where that is defined.

https://docs.python.org/3/glossary.html#term-method

 Even if it is so defined, the definition is wrong. You can define methods on
 an instance. I showed an example of an instance with its own personal
 __dir__ method, and showed that dir() ignores it if the instance belongs to
 a new-style class but uses it if it is an old-style class.

You didn't define a method, you defined a callable attribute.
Old-style classes will call those for special method overriding,
because it's the simplest thing to do. New-style classes look methods
up on the class as an optimization, but it also really complicates the
attribute semantics. The lookup strategy is explicitly defined in the
docs. pydoc is, like always, incomplete or inaccurate. See
https://docs.python.org/2/reference/datamodel.html#special-method-names

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


Re: Python is DOOMED! Again!

2015-02-01 Thread Paul Rubin
Steven D'Aprano steve+comp.lang.pyt...@pearwood.info writes:
 No apples and no oranges aren't the same thing, but if somebody is expecting 
 no apples, and I give them no oranges instead, it would be churlish for them 
 to complain that none of them are the wrong kind of fruit.

https://davedevine.wordpress.com/2011/01/20/the-sartre-joke/
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Python is DOOMED! Again!

2015-02-01 Thread Gregory Ewing

Devin Jeanpierre wrote:

I answered my own question later, by accident: Java nulls are castable
to each other if you do it explicitly (routing through Object -- e.g.
(Something)((Object) ((SomeOtherThing) null.

So in that sense, there is only one null, just with some arbitrary
compiler distinctions you can break through if you try hard enough.


You can't conclude that they all have the same runtime
representation, though. The compiler could be generating
code to convert them in response to the cast.

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


Re: Python is DOOMED! Again!

2015-02-01 Thread Paul Rubin
Steven D'Aprano steve+comp.lang.pyt...@pearwood.info writes:
 if type(ptr) == A:
 if ptr != Anil: ...
 if type(ptr) == B:
 if ptr != Bnil: ... 
 etc. That would be insane. So how does Haskell do this?

That wouldn't make sense in Haskell: the types are known at compile
time, so you wouldn't do that runtime switching on them.

 At the bottom of the page is a link to a .mov version.

Didn't see that earlier.  Managed to download and mplayer is able to
show it.  Thanks!

You might like: http://learnyouahaskell.com/chapters
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: dunder-docs (was Python is DOOMED! Again!)

2015-02-01 Thread Gregory Ewing

Steven D'Aprano wrote:

[quote]
If the object has a method named __dir__(), this method will 
be called and must return the list of attributes.

[end quote]

The first inaccuracy is that like all (nearly all?) dunder methods, Python
only looks for __dir__ on the class, not the instance itself.


It says method, not attribute, so technically
it's correct. The methods of an object are defined
by what's in its class.

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


Re: Python is DOOMED! Again!

2015-02-01 Thread Paul Rubin
Chris Angelico ros...@gmail.com writes:
 So since you can set something to Nothing regardless of type, and
 compare it against Nothing regardless of type, it doesn't really much
 matter that there are different types of Nothing. Right?

No that's not how type inference works.  If you have x = Nothing and
pass it to a function that takes a Maybe Int, type inference means the
compiler figures out that x must have type Maybe Int.  If you then also
pass x to something that takes Maybe String, you are telling the
compiler that x has two different types at the same time, so the
compiler reports a type error.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Python is DOOMED! Again!

2015-02-01 Thread Chris Angelico
On Mon, Feb 2, 2015 at 5:07 PM, Paul Rubin no.email@nospam.invalid wrote:
 Chris Angelico ros...@gmail.com writes:
 So since you can set something to Nothing regardless of type, and
 compare it against Nothing regardless of type, it doesn't really much
 matter that there are different types of Nothing. Right?

 No that's not how type inference works.  If you have x = Nothing and
 pass it to a function that takes a Maybe Int, type inference means the
 compiler figures out that x must have type Maybe Int.  If you then also
 pass x to something that takes Maybe String, you are telling the
 compiler that x has two different types at the same time, so the
 compiler reports a type error.

If you say x = 5 and pass it to a function that accepts Int or
String, the compiler knows that it's actually an Int. If you then
also pass that x to something that takes Int or List, is that legal?
If so, then 5 is separate from the or String and or List parts,
and Nothing is actually typed. If not, then it's x, not Nothing, that
has the type.

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


Re: Python is DOOMED! Again!

2015-02-01 Thread Paul Rubin
Chris Angelico ros...@gmail.com writes:
 If you say x = 5 and pass it to a function that accepts Int or
 String, the compiler knows that it's actually an Int.  If you then
 also pass that x to something that takes Int or List, is that legal?

You'd have to do that with type classes, but yeah, the compiler figures
out that x is an Int.

 If so, then 5 is separate from the or String and or List parts,
 and Nothing is actually typed. If not, then it's x, not Nothing, that
 has the type.

Not sure what you mean there.  Haskell is statically typed which means
all expressions including literals have types.  And an equality like 
x = y + z requires the two sides of the equality to have the same type.  
So if you say x = Nothing and the compiler infers (from some other place
in the program) that x has type Maybe String, then the Nothing you wrote
also has type Maybe String.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Python is DOOMED! Again!

2015-02-01 Thread Gregory Ewing

Steven D'Aprano wrote:

If I have an arbitrary pointer, and I want to check if it is safe to
dereference, how do I do it? Surely I'm not expected to write something
like:

if type(ptr) == A:
if ptr != Anil: ...
if type(ptr) == B:
if ptr != Bnil: ... 


etc. That would be insane. So how does Haskell do this?


In Haskell you would just go ahead and compare ptr
with Nothing (or more likely pattern-match it against
Nothing).

Haskell knows the type of the thing you're comparing
to, and uses type inference to select the right type
of Nothing to use.

BTW, technically, Nothing isn't really a null
pointer, it's a member of an algebraic type
defined in the standard library:

data Maybe a = Just a | Nothing

So conceptually, there is a different Nothing for
each possible type 'a' in Maybe a. But since
the Nothing constructor doesn't have any arguments,
the implementation could represent them all by
the same value if it wanted.

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


Re: dunder-docs (was Python is DOOMED! Again!)

2015-02-01 Thread Vito De Tullio
Steven D'Aprano wrote:

 Checking the REPL first would have revealed that [].__dir__ raises
 AttributeError. In other words, lists don't have a __dir__ method.

?

Python 3.4.2 (default, Nov 29 2014, 00:45:45) 
[GCC 4.8.3] on linux
Type help, copyright, credits or license for more information.
 [].__dir__()
['sort', '__contains__', '__init__', '__ge__', 'count', '__class__', 
'__format__', '__mul__', 'index', '__rmul__', '__hash__', '__iter__', 
'clear', '__subclasshook__', '__getitem__', 'reverse', 'append', '__ne__', 
'pop', '__reduce__', '__add__', 'extend', '__gt__', '__sizeof__', 
'__setattr__', '__imul__', '__dir__', '__le__', 'insert', '__repr__', 
'__str__', '__getattribute__', '__len__', '__lt__', 'remove', '__new__', 
'__reduce_ex__', 'copy', '__reversed__', '__delattr__', '__eq__', 
'__setitem__', '__iadd__', '__doc__', '__delitem__']
 


-- 
By ZeD

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


Re: Python is DOOMED! Again!

2015-02-01 Thread Chris Angelico
On Mon, Feb 2, 2015 at 4:19 PM, Gregory Ewing
greg.ew...@canterbury.ac.nz wrote:
 In Haskell you would just go ahead and compare ptr
 with Nothing (or more likely pattern-match it against
 Nothing).

So since you can set something to Nothing regardless of type, and
compare it against Nothing regardless of type, it doesn't really much
matter that there are different types of Nothing. Right?

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


Re: Python is DOOMED! Again!

2015-02-01 Thread Steven D'Aprano
Paul Rubin wrote:

 Chris Angelico ros...@gmail.com writes:
 So since you can set something to Nothing regardless of type, and
 compare it against Nothing regardless of type, it doesn't really much
 matter that there are different types of Nothing. Right?
 
 No that's not how type inference works.  If you have x = Nothing and
 pass it to a function that takes a Maybe Int, type inference means the
 compiler figures out that x must have type Maybe Int.  If you then also
 pass x to something that takes Maybe String, you are telling the
 compiler that x has two different types at the same time, so the
 compiler reports a type error.

No apples and no oranges aren't the same thing, but if somebody is expecting 
no apples, and I give them no oranges instead, it would be churlish for them 
to complain that none of them are the wrong kind of fruit.



-- 
Steve

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


Re: dunder-docs (was Python is DOOMED! Again!)

2015-02-01 Thread Steven D'Aprano
Gregory Ewing wrote:

 Steven D'Aprano wrote:
 [quote]
 If the object has a method named __dir__(), this method will
 be called and must return the list of attributes.
 [end quote]
 
 The first inaccuracy is that like all (nearly all?) dunder methods,
 Python only looks for __dir__ on the class, not the instance itself.
 
 It says method, not attribute, so technically
 it's correct. The methods of an object are defined
 by what's in its class.

Citation please. I'd like to see where that is defined.

Even if it is so defined, the definition is wrong. You can define methods on 
an instance. I showed an example of an instance with its own personal 
__dir__ method, and showed that dir() ignores it if the instance belongs to 
a new-style class but uses it if it is an old-style class.



-- 
Steven

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


Re: Python is DOOMED! Again!

2015-01-31 Thread Chris Angelico
On Sat, Jan 31, 2015 at 10:56 PM, Steven D'Aprano
steve+comp.lang.pyt...@pearwood.info wrote:
 Both ints and floats are models of the same abstract thing, namely number.
 Ideally, from a mathematically standpoint, there should be no difference
 between 23 and 23.0. Automatic coercions allow us to get a little closer to
 that ideal.

So far, I'm mostly with you. (Though if your float type is not a
perfect superset of your integer type - as in Python - then the
default up-cast from int to float, while disallowing a corresponding
implicit down-cast, seems flawed. But in concept, yes, automatic
coercion allows us to treat 23 and 23.0 as the same.)

 Arbitrary objects, on the other hand, are rarely related to strings. Given
 that we need to be able to display arbitrary objects to the human
 programmer, if only for debugging, we need to be able to *explicitly*
 convert into a string:


 py import nntplib
 py SERVER = news.gmane.org
 py server = nntplib.NNTP(SERVER)
 py str(server)
 'nntplib.NNTP instance at 0xb7bc76ec'

Here, though, I'm not so sure. Why should you be able to *type-cast*
anything to string? Python has another, and perfectly serviceable,
function for converting arbitrary objects into strings, and that's
repr(). It would make perfect sense for a language to make this
distinction much more stark:

1) str() attempts to convert something into a string. It can do this
automatically in the case of string-like objects (eg buffers, maybe
some magical things that come from databases), and can convert others
with help (eg bytes-string using an encoding parameter), but anything
else will raise an error.

2) repr() guarantees to convert anything into a string. It does this
in a relatively arbitrary fashion; you can write a helper method for
your class to make this more useful to the human.

#2 is how Python's repr already functions, so explicitly converting
arbitrary objects into strings is covered. The idea that we can str()
them as well isn't necessarily part of a sane typing system.

(Note that I'm not saying that Python got it wrong, here; just that
taking the alternate choice would also be not-wrong.)

 but doing so *implicitly* gains us nothing except the saving of a few
 keystrokes, while risking serious bugs.

Complete and automatic casting to string, I would agree. However, I
would suggest that there are a few *binary operations* which could be
more convenient if they allowed some non-strings. For instance, Pike
allows addition of strings and integers: 1 + 2 == 12, where Python
requires 1 + str(2) for the same operation. (But Pike does *not*
allow just any object there. Only a few, like numbers, can be quietly
cast on being added to strings.)

 Forcing all arbitrary objects to
 support string operations would be pointless and confusing. What could this
 possibly mean?

 server.replace('7', 'FOO')

Well duh, you would go to the server and replace the 7th stored post
with the new body 'FOO' :)

Strings have *tons* of methods. There's no way you'd want them all on
every object, and it wouldn't make sense. You definitely don't up-cast
everything to string just to use methods on them... I can't imagine
any (sane) language ever doing that.

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


Re: Python is DOOMED! Again!

2015-01-31 Thread Steven D'Aprano
Chris Angelico wrote:

 On Sat, Jan 31, 2015 at 10:56 PM, Steven D'Aprano
 steve+comp.lang.pyt...@pearwood.info wrote:
 Both ints and floats are models of the same abstract thing, namely
 number. Ideally, from a mathematically standpoint, there should be no
 difference between 23 and 23.0. Automatic coercions allow us to get a
 little closer to that ideal.
 
 So far, I'm mostly with you. (Though if your float type is not a
 perfect superset of your integer type - as in Python - then the
 default up-cast from int to float, while disallowing a corresponding
 implicit down-cast, seems flawed. But in concept, yes, automatic
 coercion allows us to treat 23 and 23.0 as the same.)

In principle, we might have a real-number type that is a perfect superset of
ints, and we might even have int versions of NAN and INF. But down-casting
real-to-integer is ambiguous, due to the need to handle any fractional
parts:

- raise an exception if the fractional part is non-zero
- truncate (round towards zero)
- round down towards -infinity
- round up toward +infinity
- round to nearest, ties to odd numbers
- round to nearest, ties to even numbers
- round to nearest, ties split randomly
- something else

One might semi-arbitrarily pick one (say, truncation) as the default when
you cast using int(x) but you need to support at least the most common
rounding modes, perhaps as separate functions.


 Arbitrary objects, on the other hand, are rarely related to strings.
 Given that we need to be able to display arbitrary objects to the human
 programmer, if only for debugging, we need to be able to *explicitly*
 convert into a string:


 py import nntplib
 py SERVER = news.gmane.org
 py server = nntplib.NNTP(SERVER)
 py str(server)
 'nntplib.NNTP instance at 0xb7bc76ec'
 
 Here, though, I'm not so sure. Why should you be able to *type-cast*
 anything to string? Python has another, and perfectly serviceable,
 function for converting arbitrary objects into strings, and that's
 repr().

Which *also* converts to a string. (Note I didn't say *cast* to a string. I
cannot imagine any meaningful definition of what casting a NNTP server
object to a str might be.)


 It would make perfect sense for a language to make this 
 distinction much more stark:
 
 1) str() attempts to convert something into a string. It can do this
 automatically in the case of string-like objects (eg buffers, maybe
 some magical things that come from databases), and can convert others
 with help (eg bytes-string using an encoding parameter), but anything
 else will raise an error.
 
 2) repr() guarantees to convert anything into a string. It does this
 in a relatively arbitrary fashion; you can write a helper method for
 your class to make this more useful to the human.
 
 #2 is how Python's repr already functions, so explicitly converting
 arbitrary objects into strings is covered. The idea that we can str()
 them as well isn't necessarily part of a sane typing system.
 
 (Note that I'm not saying that Python got it wrong, here; just that
 taking the alternate choice would also be not-wrong.)

I agree with all of that. And for what it is worth, a class can refuse to
convert to str while still supporting repr:

py class K(object):
... def __str__(self): raise TypeError
... def __repr__(self): return Stuff and things. Mostly stuff.
...
py k = K()
py str(k)
Traceback (most recent call last):
  File stdin, line 1, in module
  File stdin, line 2, in __str__
TypeError
py repr(k)
'Stuff and things. Mostly stuff.'


But by default, Python will fallback on __repr__ if __str__ doesn't exist,
or __str__ if __repr__ doesn't exist, or both. Or something. (I always
forget what the rules are exactly.)


 but doing so *implicitly* gains us nothing except the saving of a few
 keystrokes, while risking serious bugs.
 
 Complete and automatic casting to string, I would agree. However, I
 would suggest that there are a few *binary operations* which could be
 more convenient if they allowed some non-strings. For instance, Pike
 allows addition of strings and integers: 1 + 2 == 12, where Python
 requires 1 + str(2) for the same operation. (But Pike does *not*
 allow just any object there. Only a few, like numbers, can be quietly
 cast on being added to strings.)

I'm surprised you're not into Perl, with an attitude like that. A sick,
disgusting, despicably perverted attitude. *wink*

But seriously, I can see some uses there, but frankly why bother to make an
exception for ints when you require all other types to have an explicit
coercion?

The problem with string/int automatic coercions is that there are lots of
answers but none of them are obviously the right answer:

1 + 1 -- 11 or 2?

1a + 1 -- 2 like Perl does, or 1a1 like Javascript does?

Do you strip out all non-numeric characters, or truncate at the first
non-numeric character?

Should you perhaps be a little more flexible and allow common mistypings
like O for 0 and l for 1? How about whitespace?



-- 
Steven

-- 

Re: Python is DOOMED! Again!

2015-01-31 Thread Ethan Furman
On 01/31/2015 07:16 PM, Steven D'Aprano wrote:
 
 But by default, Python will fallback on __repr__ if __str__ doesn't exist,
 or __str__ if __repr__ doesn't exist, or both. Or something. (I always
 forget what the rules are exactly.)

If __str__ is missing, __repr__ is called.

If __repr__ is missing, object.__repr__ (or some intermediate base class' 
__repr__) is called.

--
~Ethan~



signature.asc
Description: OpenPGP digital signature
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Python is DOOMED! Again!

2015-01-31 Thread Paul Rubin
Steven D'Aprano steve+comp.lang.pyt...@pearwood.info writes:
 Some degree of weakness in a type system is not necessarily bad. Even the
 strongest of languages usually allow a few exceptions, such as numeric
 coercions.

Haskell doesn't have automatic coercions of any sort.  You have to call
a conversion function if you want to turn an Int into an Integer.

 I've never come across a language that has pointers which insists on
 having a separate Nil pointer for ever pointer type

Haskell's idiomatic substitute for a null pointer is a Nothing value
(like Python's None) and there's a separate one for every type.  The FFI
offers actual pointers (Foreign.Ptr) and there is a separate nullPtr
for every type.

 the compiler will allow Nil to be used for any pointer type. Anything
 else would be impractical.

It's completely practical: polymorphism and type inference get you the
value you want with usually no effort on your part.

 What if you add two empty objects?
 js {} + {}

OMG, javascript is worse than I thought

 https://www.destroyallsoftware.com/talks/wat

Can't view, needs flash. :(

Try this instead (NFSW): https://www.youtube.com/watch?v=FJ7QsEytQq4
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Python is DOOMED! Again!

2015-01-31 Thread Devin Jeanpierre
Sorry, sort of responding to both of you.

On Sat, Jan 31, 2015 at 10:12 PM, Paul Rubin no.email@nospam.invalid wrote:
 Steven D'Aprano steve+comp.lang.pyt...@pearwood.info writes:
 Some degree of weakness in a type system is not necessarily bad. Even the
 strongest of languages usually allow a few exceptions, such as numeric
 coercions.

 Haskell doesn't have automatic coercions of any sort.  You have to call
 a conversion function if you want to turn an Int into an Integer.

Yeah. In fact, it isn't very compatible with the ML/Haskell type
system to automatically convert, because it does weird things to type
inference and type unification. So this is common in that language
family.

That said, Haskell (and the rest) do have a sort of type coercion, of
literals at compile time (e.g. 3 can be an Integer or a Double
depending on how you use it.)

BTW it's weird that in this thread, and in the programmer community at
large, int-string is considered worse than int-float, when the
former is predictable and reversible, while the latter is lossy and
can cause subtle bugs. Although at least we don't have ten+ types with
sixty different spellings which change from platform to platform, and
all of which automatically coerce despite massive and outrageous
differences in representable values. (Hello, C.)

 I've never come across a language that has pointers which insists on
 having a separate Nil pointer for ever pointer type

 Haskell's idiomatic substitute for a null pointer is a Nothing value
 (like Python's None) and there's a separate one for every type.  The FFI
 offers actual pointers (Foreign.Ptr) and there is a separate nullPtr
 for every type.

For that matter, how is this (first part) different from, say, Java?

It's really only dynamically typed languages that have a single null
value of a single type. Maybe I misunderstand the original statement.

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


Re: Python is DOOMED! Again!

2015-01-31 Thread Steven D'Aprano
Mario Figueiredo wrote:

 In article 54ca5bbf$0$12992$c3e8da3$54964...@news.astraweb.com,
 steve+comp.lang.pyt...@pearwood.info says...
 
 Why should I feel guilty? You wrote:
 
 
 Static analysis cannot and should not clutter executable code.
 
 
 But what are type declarations in statically typed languages like C,
 Pascal, Haskell, etc.? They are used by the compiler for static analysis.
 The same applies to type declarations in dynamically typed languages like
 Cobra and Julia. And yet, there they are, in the executable code.
 
 So there are a whole lot of languages, going all the way back to 1950s
 languages like Fortran, to some of the newest languages which are only a
 few years old like Go, both dynamically typed and statically typed, which
 do exactly what you say languages cannot and should not do: they put
 type information used for static analysis there in the code.
 
 You are confusing static analysis with compile time checking which
 produces side-effects like implicit conversion for instance and that
 affects the resulting binary code. Something that Python won't do with
 type annotations. And something that Julia, Scala or C does.

I'm not confusing anything. I'm fully aware of the differences between what
C or Pascal does and what Python does. But I'm also aware of the
similarities.


 This is also the first time I hear compilation time mentioned as static
 analysis.

Compilation consists of many different tasks. They may be explicitly handled
by different tools, or implicitly handled by a single tool. That tool may
combine them into a single phase, or keep them separate. These are all
implementation details: different compilers for the same language could do
this differently.

Typical tasks for a compiler, in no particular order:

- lexing and parsing of source code
- type analysis and checking
- code generation
- linking to external libraries
- optimisation

For languages like C and Pascal, the type checking is typically done
statically at compile-time. So of course they include static analysis. The
compiler hasn't compiled the code yet, so it only has the source code to
work with! A type error will halt the compiler and stop the program from
compiling.


 To be clear, type declarations in Julia, Scala, C have the potential to
 produce side-effects, can result in optimized code and can result in
 compile time errors or warnings. Type annotations in Python are instead
 completely ignored by the interpreter. They do nothing of the above.
 They do not participate in code execution.

As as been pointed out repeatedly, Python annotations DO participate in the
code execution: the annotations are created and stored in the function
object at runtime, and one could easily create a library to use those
annotations at runtime for runtime type-checks.


 As I said, these languages disagree with you. You are not just arguing
 against Guido, but against the majority of programming language designers
 for 60+ years.
 
 You are right. I'm not arguing against Guido. I have yet to hear his
 opinion on your or mine arguments. I'm not arguing against the majority
 of programming languages either, because they agree with me.

Do you really expect us to believe that the majority of programming
languages put type declarations in docstrings, or a second file separate
from the source of the function? Okay, let's see some examples. Here is a
list of languages still well-known enough that people use them:

http://rosettacode.org/wiki/Category:Programming_Languages


How many of them use docstrings as the only way to declare the type of a
variable? How many use header files as the sole way to declare the type of
variables?

I'm feeling generous. You don't even need to show a *majority* (50%). I'd be
amazed if you can find *ten percent* of those languages that agree with
you.



-- 
Steven

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


Re: Python is DOOMED! Again!

2015-01-31 Thread Chris Angelico
On Sun, Feb 1, 2015 at 2:16 PM, Steven D'Aprano
steve+comp.lang.pyt...@pearwood.info wrote:
 Chris Angelico wrote:

 On Sat, Jan 31, 2015 at 10:56 PM, Steven D'Aprano
 steve+comp.lang.pyt...@pearwood.info wrote:
 Both ints and floats are models of the same abstract thing, namely
 number. Ideally, from a mathematically standpoint, there should be no
 difference between 23 and 23.0. Automatic coercions allow us to get a
 little closer to that ideal.

 So far, I'm mostly with you. (Though if your float type is not a
 perfect superset of your integer type - as in Python - then the
 default up-cast from int to float, while disallowing a corresponding
 implicit down-cast, seems flawed. But in concept, yes, automatic
 coercion allows us to treat 23 and 23.0 as the same.)

 In principle, we might have a real-number type that is a perfect superset of
 ints, and we might even have int versions of NAN and INF. But down-casting
 real-to-integer is ambiguous, due to the need to handle any fractional
 parts:

 - raise an exception if the fractional part is non-zero
 - truncate (round towards zero)
 - round down towards -infinity
 - round up toward +infinity
 - round to nearest, ties to odd numbers
 - round to nearest, ties to even numbers
 - round to nearest, ties split randomly
 - something else

 One might semi-arbitrarily pick one (say, truncation) as the default when
 you cast using int(x) but you need to support at least the most common
 rounding modes, perhaps as separate functions.

Agreed; but the trap here is that there are equivalent problems when
converting integers to floating point - just more subtle, because they
don't happen in the low ranges of values. In the same way that a
UTF-16 string representation has more subtle problems than an ASCII
string representation (because it's easy to test your code on foreign
text and still not realize that it has issues with astral
characters), casting int to float is subtle because you'll probably do
all your testing on numbers less than 2**53. It might take a lot of
tracking-down work before you finally discover that there's one place
in your code where you do division with / instead of //, and you get
back a float, and then only when your integers are really huge (maybe
you encode an eight-digit date, four-digit time, then a three-digit
country code, and finally a two-digit incrementing number) do you
actually start losing precision. The bulk of Python programs will
never run into this; yet we do have an arbitrary-precision integer
type, we're not like ECMAScript with a single Number type.

 Arbitrary objects, on the other hand, are rarely related to strings.
 Given that we need to be able to display arbitrary objects to the human
 programmer, if only for debugging, we need to be able to *explicitly*
 convert into a string:


 py import nntplib
 py SERVER = news.gmane.org
 py server = nntplib.NNTP(SERVER)
 py str(server)
 'nntplib.NNTP instance at 0xb7bc76ec'

 Here, though, I'm not so sure. Why should you be able to *type-cast*
 anything to string? Python has another, and perfectly serviceable,
 function for converting arbitrary objects into strings, and that's
 repr().

 Which *also* converts to a string. (Note I didn't say *cast* to a string. I
 cannot imagine any meaningful definition of what casting a NNTP server
 object to a str might be.)

Sure, but Python doesn't really have a way to spell convert this to a
string if it's already basically stringy, otherwise raise TypeError.
You can do that for other types like int, but not for string, because
you can always call str() on something.

 I agree with all of that. And for what it is worth, a class can refuse to
 convert to str while still supporting repr:

 py class K(object):
 ... def __str__(self): raise TypeError
 ... def __repr__(self): return Stuff and things. Mostly stuff.
 ...
 py k = K()
 py str(k)
 Traceback (most recent call last):
   File stdin, line 1, in module
   File stdin, line 2, in __str__
 TypeError
 py repr(k)
 'Stuff and things. Mostly stuff.'

Hmm. Sure you can do that, but is that just part of the freedom you
have to shoot yourself in the foot? Would that be considered an
ill-behaved class?

 Complete and automatic casting to string, I would agree. However, I
 would suggest that there are a few *binary operations* which could be
 more convenient if they allowed some non-strings. For instance, Pike
 allows addition of strings and integers: 1 + 2 == 12, where Python
 requires 1 + str(2) for the same operation. (But Pike does *not*
 allow just any object there. Only a few, like numbers, can be quietly
 cast on being added to strings.)

 I'm surprised you're not into Perl, with an attitude like that. A sick,
 disgusting, despicably perverted attitude. *wink*

 But seriously, I can see some uses there, but frankly why bother to make an
 exception for ints when you require all other types to have an explicit
 coercion?

Ints, floats, and any user-defined type that chooses to ask for it;

dunder-docs (was Python is DOOMED! Again!)

2015-01-31 Thread Rustom Mody
On Sunday, February 1, 2015 at 10:15:13 AM UTC+5:30, Ethan Furman wrote:
 On 01/31/2015 07:16 PM, Steven D'Aprano wrote:
  
  But by default, Python will fallback on __repr__ if __str__ doesn't exist,
  or __str__ if __repr__ doesn't exist, or both. Or something. (I always
  forget what the rules are exactly.)
 
 If __str__ is missing, __repr__ is called.
 
 If __repr__ is missing, object.__repr__ (or some intermediate base class' 
 __repr__) is called.
 
 --
 ~Ethan~

The other day I was taking a class in which I was showing
- introspection for discovering -- help, type, dir etc at the repl
- mapping of surface syntax to internals eg. a + b ←→ a.__add__(b)

And a student asked me the diff between
dir([])
and
[].__dir__()

I didnt know what to say...
Now surely the amount of python I dont know is significantly larger than what I 
know
Still it would be nice to have surface-syntax ←→ dunder-magic more
systematically documented
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Python is DOOMED! Again!

2015-01-31 Thread Steven D'Aprano
random...@fastmail.us wrote:

 On Thu, Jan 29, 2015, at 10:56, Steven D'Aprano wrote:
 Bar language, on the other hand, tries extremely hard to ensure that
 every
 type is automatically able to be coerced into every other type. The
 coercion might not do what you expect, but it will do *something*:
 
 See, this is where the concept falls apart. Many people will define a
 dynamically-typed language as weakly-typed *only if* the result of the
 automatic type coercion is unexpected/distasteful/fattening, 

Oh, the curse of the discontinuous mind! :-)

https://richarddawkins.net/2013/01/the-tyranny-of-the-discontinuous-mind-christmas-2011/

A concept doesn't fall apart just because a few people are unable to use it
correctly. We may not be able to distinguish the exact moment when a child
becomes an adult (except, of course, by picking some arbitrary culturally
sanctioned age) but the concepts of child and adult are meaningful. And so
it is with strong and weak typing: people of good faith may nevertheless
disagree where the dividing line should be draw, or even whether a dividing
line is meaningful, but the concept of typing strength remains meaningful.


 even if it 
 is well-defined and predictable according to the rules of the language.
 Which makes it a matter of personal taste.


Some degree of weakness in a type system is not necessarily bad. Even the
strongest of languages usually allow a few exceptions, such as numeric
coercions. I've never come across a language that has pointers which
insists on having a separate Nil pointer for ever pointer type: the
compiler will allow Nil to be used for any pointer type. Anything else
would be impractical.

Unfortunately, weakly typed languages get a bad reputation because so few of
them have well-defined rules that can be derived without having to learn a
bunch of arbitrary rules. Consider Javascript. What happens when you add
two arrays?

js [1, 2, 3] + [4]
1,2,34

If it makes no sense to you, consider this:

js typeof([1, 2, 3] + [4])
string

If it still makes no sense to you, you're not alone.

How about an array and an object?

js [] + {}  //empty array plus empty object
[object Object]
js {} + [] //empty object plus empty array
0

What if you add two empty objects?

js {} + {}
NaN

More here:

https://www.destroyallsoftware.com/talks/wat


 Bar will never give you a TypeError. I think we can agree that Bar is a
 *very weakly typed* language.
 
 Statically typed lanugages by definition can never give you a TypeError

They can't give you a *runtime* TypeError, but the compiler can give you a
compile-time type error. (Sorry for the misleading use of TypeError as in
the Python exception, I was thinking more broadly than just runtime
exceptions.)


 - there are no runtime conversions that can succeed or fail based on the
 type of the arguments. What makes a statically typed language strong or
 weak? Are statically typed languages always weak?

Statically typed languages tend to be strong. The point of doing static type
analysis is to prohibit ill-defined operations at compile-time, rather than
have the compiler mindlessly (say) write a 16-byte struct where a 2-byte
int is expected. But I don't think that it is necessarily the case that
statically-typed languages *must* be strongly typed, or dynamically-typed
languages weak.


 There are degrees of strength, and I think that Python comes closer to
 the
 Foo end than the Bar end. There are few automatic coercions, and most of
 those are in the numeric tower according to the usual mathematical rules.
 
 Why is converting an int to a float when passed to a math function
 better than converting any object to a string when passed to a string
 function?

Both ints and floats are models of the same abstract thing, namely number.
Ideally, from a mathematically standpoint, there should be no difference
between 23 and 23.0. Automatic coercions allow us to get a little closer to
that ideal.

Arbitrary objects, on the other hand, are rarely related to strings. Given
that we need to be able to display arbitrary objects to the human
programmer, if only for debugging, we need to be able to *explicitly*
convert into a string:


py import nntplib
py SERVER = news.gmane.org
py server = nntplib.NNTP(SERVER)
py str(server)
'nntplib.NNTP instance at 0xb7bc76ec'

but doing so *implicitly* gains us nothing except the saving of a few
keystrokes, while risking serious bugs. Forcing all arbitrary objects to
support string operations would be pointless and confusing. What could this
possibly mean?

server.replace('7', 'FOO')


We shouldn't insist that server objects support string methods, since that
would preempt them using methods of the same name for their own purposes,
and would needlessly clutter their API. We shouldn't want server objects to
automatically coerce into a string (why a string? why not a list or a dict
or a float?) because there is no real benefit to doing so. It can only
cause confusion.


-- 
Steven

-- 

Re: Python is DOOMED! Again!

2015-01-30 Thread Ian Kelly
On Fri, Jan 30, 2015 at 11:42 AM, Mario Figueiredo mar...@gmail.com wrote:
 To be clear, type declarations in Julia, Scala, C have the potential to
 produce side-effects, can result in optimized code and can result in
 compile time errors or warnings. They also affect runtime evaluation as
 you could easily attest if you input a float into a function expecting
 an int, whereas in Python the float will be gladly accepted and will
 only fail at the point in code where its interface won't match the
 statement.

At least for C, as I noted in a previous post, it is simply not true
that they are used for runtime evaluation. For example:

 import ctypes
 libc = ctypes.CDLL(libc.so.6)
 libc.abs(ctypes.c_double(123.456))
2093824448

The C compiler may complain about it, but that's a compile-time static
check, no different from the sort of checks that PEP 484 seeks to add
to Python.

 Meanwhile, type annotations in Python are instead completely ignored by
 the interpreter. They do nothing of the above. They do not participate
 in code generation and execution.

But unlike C, Python lets you easily implement this yourself if you want to.

 def runtime_type_check(f):
...   @functools.wraps(f)
...   def wrapper(**args):
... for arg, value in args.items():
...   if arg in f.__annotations__:
... if not isinstance(value, f.__annotations__[arg]):
...   raise TypeError(Arg %s expected %s, got %s
...   % (arg, f.__annotations__[arg].__name__, type(arg).__name__))
... return f(**args)
...   return wrapper
...
 @runtime_type_check
... def add(x:int, y:int) - int:
...   return x + y
...
 add(x=hello, y=world)
Traceback (most recent call last):
  File stdin, line 1, in module
  File stdin, line 8, in wrapper
TypeError: Arg y expected int, got str

(This could of course be extended for positional arguments and more
complex annotations, but I wanted to keep it simple for the purpose of
the example.)
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Python is DOOMED! Again!

2015-01-30 Thread Mario Figueiredo
In article 54ca5bbf$0$12992$c3e8da3$54964...@news.astraweb.com, 
steve+comp.lang.pyt...@pearwood.info says...
 
 Why should I feel guilty? You wrote:
 
 
 Static analysis cannot and should not clutter executable code.
 
 
 But what are type declarations in statically typed languages like C, Pascal,
 Haskell, etc.? They are used by the compiler for static analysis. The same
 applies to type declarations in dynamically typed languages like Cobra and
 Julia. And yet, there they are, in the executable code.
 
 So there are a whole lot of languages, going all the way back to 1950s
 languages like Fortran, to some of the newest languages which are only a
 few years old like Go, both dynamically typed and statically typed, which
 do exactly what you say languages cannot and should not do: they put type
 information used for static analysis there in the code.

You are confusing static analysis with compile time checking which 
produces side-effects like implicit conversion for instance and that 
affects the resulting binary code. Something that Python won't do with 
type annotations. And something that Julia, Scala or C does.

This is also the first time I hear compilation time mentioned as static 
analysis.

To be clear, type declarations in Julia, Scala, C have the potential to 
produce side-effects, can result in optimized code and can result in 
compile time errors or warnings. Type annotations in Python are instead 
completely ignored by the interpreter. They do nothing of the above. 
They do not participate in code execution.

 As I said, these languages disagree with you. You are not just arguing
 against Guido, but against the majority of programming language designers
 for 60+ years.

You are right. I'm not arguing against Guido. I have yet to hear his 
opinion on your or mine arguments. I'm not arguing against the majority 
of programming languages either, because they agree with me.

I'm arguing with you.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Python is DOOMED! Again!

2015-01-30 Thread Mario Figueiredo
In article mailman.18277.1422557674.18130.python-l...@python.org, 
breamore...@yahoo.co.uk says...
 
 No, they're not always weakly typed.  The aim of the spreadsheet put up 
 by Skip was to sort out (roughly) which languages belong in which camp. 
   I do not regard myself as suitably qualified to fill the thing out. 
 Perhaps by now others have?


It would help that if instead of weakly typed or strongly typed box, 
they could be classified comparatively to each other. The terms are 
better suited to describe two languages as they stand to each other.

Weakly Typed -- Strongly Typed

From C to Lisp (arguably) with languages closer to each other indicating 
a more similar type system model.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Python is DOOMED! Again!

2015-01-30 Thread Mario Figueiredo
In article 54ca5bbf$0$12992$c3e8da3$54964...@news.astraweb.com, 
steve+comp.lang.pyt...@pearwood.info says...
 
 
 Why should I feel guilty? You wrote:
 
 
 Static analysis cannot and should not clutter executable code.
 
 
 But what are type declarations in statically typed languages like C, Pascal,
 Haskell, etc.? They are used by the compiler for static analysis. The same
 applies to type declarations in dynamically typed languages like Cobra and
 Julia. And yet, there they are, in the executable code.
 
 So there are a whole lot of languages, going all the way back to 1950s
 languages like Fortran, to some of the newest languages which are only a
 few years old like Go, both dynamically typed and statically typed, which
 do exactly what you say languages cannot and should not do: they put type
 information used for static analysis there in the code.

(Sorry if I'm late...)

You are comparing static analysis with compile time checking which 
can result in implicit conversions and that can affect the resulting
binary code. Something that Python won't do with type annotations. And
something that Julia, Scala or C does.

This is also the first time I hear compilation mentioned as static 
analysis. But I suppose... After all it does perform a crude form of 
static analysis as a natural consequence of compile time checks, besides 
doing a whole bunch of other things that aren't static analysis. A dog 
has four legs and two eyes. So does an elephant. I suppose you are going 
to argue with me that a dog is an elephant after all.

To be clear, type declarations in Julia, Scala, C have the potential to 
produce side-effects, can result in optimized code and can result in 
compile time errors or warnings. They also affect runtime evaluation as 
you could easily attest if you input a float into a function expecting 
an int, whereas in Python the float will be gladly accepted and will 
only fail at the point in code where its interface won't match the 
statement.

Meanwhile, type annotations in Python are instead completely ignored by 
the interpreter. They do nothing of the above. They do not participate 
in code generation and execution.

 As I said, these languages disagree with you. You are not just arguing
 against Guido, but against the majority of programming language designers
 for 60+ years.

You are right. I'm not arguing against Guido. I have yet to hear his 
opinion on what you are saying, so I don't even know if I should want to
argue with him. And I'm not arguing against the majority of programming
languages either, because as much as you try I have yet to hear an 
argument from you that convinces me they don't agree with me.

No. I'm arguing with you.



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


Re: Python is DOOMED! Again!

2015-01-30 Thread Skip Montanaro
On Fri, Jan 30, 2015 at 12:50 PM, Mario Figueiredo mar...@gmail.com wrote:

 It would help that if instead of weakly typed or strongly typed box,
 they could be classified comparatively to each other. The terms are
 better suited to describe two languages as they stand to each other.

 Weakly Typed -- Strongly Typed


The spreadsheet should be world-writable. Knock yourself out. :-)

http://preview.tinyurl.com/kcrcq4y

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


Re: Python is DOOMED! Again!

2015-01-29 Thread Ian Kelly
On Thu, Jan 29, 2015 at 1:34 AM, Mario Figueiredo mar...@gmail.com wrote:
 In article 54c980cd$0$12981$c3e8da3$54964...@news.astraweb.com,
 steve+comp.lang.pyt...@pearwood.info says...

 Ian, that's obvious. Just open your eyes:

 Scala
 def addInt( a:Int, b:Int ) : Int

 Python
 def addInt( a:int, b:int ) - int:


 They're COMPLETELY different. In Scala they are *type declarations*, not
 annotations. We're talking about annotations, not declarations. They're as
 different as cheese and a very slightly different cheese. Do try to keep
 up.

 *wink*

 The sarcasm is unnecessary. They are different yes. Are you on purpose
 confusing the syntax of a feature with its meaning? Because while you
 have a very similar syntax between Julia, Scala and Python. Their
 meanings are very different.

 I think it is obvious to anyone that if a feature like type annotations
 are meant to be EVALUATED AT RUNTIME (and I myself gave you another
 example of Julia), it makes every sense for that feature to be a part of
 the programming language syntax. I could never argue against Julia or
 Scala type annotations on that basis. The syntax is an integral part of
 the executable code.

Okay, I don't know enough about Scala's type system to discuss that
example in this context, so I won't try to. Let's instead focus on
another of the languages you listed: C. C includes types in its
syntax, which it uses for static analysis at compile time. At runtime,
it uses them for ... nothing. In fact, if you examine a compiled C
binary, you won't even find any type information in there. If you
dynamically load a C library containing a function that takes a
double, and you pass it a long, it won't even blink. It will just
assume that the data it received represents a double and proceed from
there.

Now with PEP 484 type annotations on the other hand, while the
suggested static analysis tools aren't used at runtime, the
annotations themselves are available to be evaluated at runtime. If
you want to write a decorator that examines the types of the arguments
of the function it decorates and does something nifty with them
(automatic registration of overloaded functions using PEP 443
generics, perhaps), there is absolutely nothing stopping you from
doing that.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Python is DOOMED! Again!

2015-01-29 Thread Steven D'Aprano
Mario Figueiredo wrote:

 In article 54c980cd$0$12981$c3e8da3$54964...@news.astraweb.com,
 steve+comp.lang.pyt...@pearwood.info says...
 
 Ian, that's obvious. Just open your eyes:
 
 Scala
 def addInt( a:Int, b:Int ) : Int
 
 Python
 def addInt( a:int, b:int ) - int:
 
 
 They're COMPLETELY different. In Scala they are *type declarations*, not
 annotations. We're talking about annotations, not declarations. They're
 as different as cheese and a very slightly different cheese. Do try to
 keep up.
 
 *wink*
 
 The sarcasm is unnecessary. They are different yes. Are you on purpose
 confusing the syntax of a feature with its meaning? Because while you
 have a very similar syntax between Julia, Scala and Python. Their
 meanings are very different.

No they aren't. Their primary meaning is to declare the type used by the
parameter.



 I think it is obvious to anyone that if a feature like type annotations
 are meant to be EVALUATED AT RUNTIME (and I myself gave you another
 example of Julia), it makes every sense for that feature to be a part of
 the programming language syntax. I could never argue against Julia or
 Scala type annotations on that basis. The syntax is an integral part of
 the executable code.

Ah, you mean just like Python, where annotations are evaluated at runtime,
just like Julia:

py def spam(n:int)- getattr(sys, 'version'):
... return spam*n
...
py spam.__annotations__
{'n': class 'int', 'return': '3.3.0rc3 (default, Sep 27 2012, 18:44:58)
\n[GCC 4.1.2 20080704 (Red Hat 4.1.2-52)]'}


Python has had annotations for over five years. They are evaluated at
runtime. They will continue to be evaluated at runtime.

One of the benefits of this system is that people may write *runtime*
type-checkers that use the same annotations that the lexical analysis tools
will use.

It is possible that you aren't following the meaning of this. The idea
behind PEP 484 is that Python will standardise on a single expected syntax
for type-hints, so that all of the following tools can agree on a set of
basic rules for interpreting the annotations:

- lexical type-checkers (those that operate on only the source code, 
  with no runtime information)
- alternate Python compilers, like MyPy, which perform compile-time 
  type checking
- IDEs and text editors, which may use lexical type info available
  in the source code to flag errors and offer auto-completion
- stand-alone tools such as correctness provers
- documentation generators
- runtime type-checkers
- runtime introspection tools

and more.

One of the rules is that the *standard* type-hints have to be simple enough
that a purely lexical tool like an IDE or editor can understand it without
needing to run arbitrary code. But the annotations themselves can be
arbitrarily complex Python expressions. Its just that then the tools
expecting type-hints won't be able to process those annotations from the
source code alone. Runtime introspection tools will not care, because they
don't see the expression, they only see the result of evaluating that
expression.


 But when a feature is not meant for runtime evaluation, why should it
 clutter your executable code? Make me understand your reasoning?

Perhaps you should ask the designers of Pascal, Go, Scheme, C, D, F# and all
those dozens and hundreds of other languages. They have a feature which is
not evaluated at runtime -- the type declarations -- and they put it in the
source code.

And why shouldn't they? Type declarations are source code!


 Your list of programming languages is just plain wrong. To my knowledge
 (there's a few languages there I don't know and never used), none of
 those languages implement anything like Python. 

Not so.


 Type annotations in 
 python are entirely ignored by the interpreter except to make them 
 available to external libraries. 


That is incorrect.

Perhaps you are confusing by the fact that the Python interpreter doesn't
give any meaning to annotations? It still evaluates them at runtime, and
stores the result in the function object.



-- 
Steven

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


Re: Python is DOOMED! Again!

2015-01-29 Thread Mario Figueiredo
In article 54c980cd$0$12981$c3e8da3$54964...@news.astraweb.com, 
steve+comp.lang.pyt...@pearwood.info says...
 
 Ian, that's obvious. Just open your eyes:
 
 Scala
 def addInt( a:Int, b:Int ) : Int
 
 Python
 def addInt( a:int, b:int ) - int:
 
 
 They're COMPLETELY different. In Scala they are *type declarations*, not
 annotations. We're talking about annotations, not declarations. They're as
 different as cheese and a very slightly different cheese. Do try to keep
 up.
 
 *wink*

The sarcasm is unnecessary. They are different yes. Are you on purpose 
confusing the syntax of a feature with its meaning? Because while you 
have a very similar syntax between Julia, Scala and Python. Their 
meanings are very different.

I think it is obvious to anyone that if a feature like type annotations 
are meant to be EVALUATED AT RUNTIME (and I myself gave you another 
example of Julia), it makes every sense for that feature to be a part of 
the programming language syntax. I could never argue against Julia or 
Scala type annotations on that basis. The syntax is an integral part of 
the executable code.

But when a feature is not meant for runtime evaluation, why should it 
clutter your executable code? Make me understand your reasoning?

Your list of programming languages is just plain wrong. To my knowledge 
(there's a few languages there I don't know and never used), none of 
those languages implement anything like Python. Type annotations in 
python are entirely ignored by the interpreter except to make them 
available to external libraries. This is a feature that I have seen 
implemented in numerous languages as a part of doc-like comments. Never, 
ever, as a part ofthe language syntax.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Python is DOOMED! Again!

2015-01-29 Thread Mark Lawrence

On 29/01/2015 08:23, Mario Figueiredo wrote:

In article mailman.18229.1422469034.18130.python-l...@python.org,
breamore...@yahoo.co.uk says...


C and C++ are weakly and statically typed languages.  Python is a
strongly and dynamically typed language.  Therefore anything following
based on the above paragraph alone is wrong.



Indeed. I confused strongly/weakly with static. I feel a a bit
embarrased by it. My apologies.


Accepted, from me anyhow. Please remember the only person who never 
makes a mistake never does anything :)




But no. Nothing that follows from that paragraph is wrong, just because
of that mistake.


I should have emphasied the word *alone*, sorry about that.



It still stands that list was artifically created to make it look like
type annotations on top of executable code is a feature of nearly every
language in the book. When it is not!

Most particularly when it comes to statically typed languages, wich
Steven didn't feel guilty of including there.



I don't know enough about most other languages to comment, I'll leave 
that to the various gurus.


--
My fellow Pythonistas, ask not what our language can do for you, ask
what you can do for our language.

Mark Lawrence

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


Re: Python is DOOMED! Again!

2015-01-29 Thread Mario Figueiredo
In article mailman.18229.1422469034.18130.python-l...@python.org, 
breamore...@yahoo.co.uk says...
 
 C and C++ are weakly and statically typed languages.  Python is a 
 strongly and dynamically typed language.  Therefore anything following 
 based on the above paragraph alone is wrong.
 

Indeed. I confused strongly/weakly with static. I feel a a bit 
embarrased by it. My apologies.

But no. Nothing that follows from that paragraph is wrong, just because 
of that mistake.

It still stands that list was artifically created to make it look like 
type annotations on top of executable code is a feature of nearly every 
language in the book. When it is not!

Most particularly when it comes to statically typed languages, wich 
Steven didn't feel guilty of including there.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Python is DOOMED! Again!

2015-01-29 Thread random832
On Thu, Jan 29, 2015, at 10:56, Steven D'Aprano wrote:
 Bar language, on the other hand, tries extremely hard to ensure that
 every
 type is automatically able to be coerced into every other type. The
 coercion might not do what you expect, but it will do *something*:

See, this is where the concept falls apart. Many people will define a
dynamically-typed language as weakly-typed *only if* the result of the
automatic type coercion is unexpected/distasteful/fattening, even if it
is well-defined and predictable according to the rules of the language.
Which makes it a matter of personal taste.

 Bar will never give you a TypeError. I think we can agree that Bar is a
 *very weakly typed* language.

Statically typed lanugages by definition can never give you a TypeError
- there are no runtime conversions that can succeed or fail based on the
type of the arguments. What makes a statically typed language strong or
weak? Are statically typed languages always weak?

 
 There are degrees of strength, and I think that Python comes closer to
 the
 Foo end than the Bar end. There are few automatic coercions, and most of
 those are in the numeric tower according to the usual mathematical rules.

Why is converting an int to a float when passed to a math function
better than converting any object to a string when passed to a string
function?
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Python is DOOMED! Again!

2015-01-29 Thread random832
On Wed, Jan 28, 2015, at 13:16, Mark Lawrence wrote:
 C and C++ are weakly and statically typed languages.

strong typing has no meaning at all, and weak typing means anything
I don't like.

The fact that you can add an int and a float, or that you can use any
object as a boolean, would make python weakly typed in some people's
eyes.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Python is DOOMED! Again!

2015-01-29 Thread Steven D'Aprano
random...@fastmail.us wrote:

 On Wed, Jan 28, 2015, at 13:16, Mark Lawrence wrote:
 C and C++ are weakly and statically typed languages.
 
 strong typing has no meaning at all, and weak typing means anything
 I don't like.

I see you've been reading Chris Smith's essay on typing :-)

https://cdsmith.wordpress.com/2011/01/09/an-old-article-i-wrote/

I think his essay is excellent, but his claim that strong and weak typing is
a meaningless distinction is excessive.

 The fact that you can add an int and a float, or that you can use any
 object as a boolean, would make python weakly typed in some people's
 eyes.

I think they would be wrong. Well, mostly wrong. But a little bit right.

We should agree that there is no universal, hard distinction between strong
and weak typing. But the names already suggest that -- there is no dividing
line between strong and weak. But we can certainly *rank* languages by
degrees of strength according to some standard, and if we agree on the
standard, we should agree on the rankings.

Even if we don't agree on a standard, we can surely agree on the two extreme
cases. Let's call them Foo and Bar language. They're both typed languages.

Foo language has no automatic coercions at all. Every type is utterly
distinct. The only way to convert a value of one type to another is with an
explicit XtoY function. Surely we can agree that Foo is a *very strongly
typed* language? (Perhaps even too strong?)

Bar language, on the other hand, tries extremely hard to ensure that every
type is automatically able to be coerced into every other type. The
coercion might not do what you expect, but it will do *something*:

{'a': 100, 'b': 300} + 5.0
= 7.0

[1, 2, 3] + hello
= [1, 2, 3, 'h', 'e', 'l', 'l', 'o']

hello + [1, 2, 3]
= hello[1, 2, 3]

Bar will never give you a TypeError. I think we can agree that Bar is a
*very weakly typed* language.

There are degrees of strength, and I think that Python comes closer to the
Foo end than the Bar end. There are few automatic coercions, and most of
those are in the numeric tower according to the usual mathematical rules.


-- 
Steven

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


Re: Python is DOOMED! Again!

2015-01-29 Thread Steven D'Aprano
Mario Figueiredo wrote:

 In article mailman.18229.1422469034.18130.python-l...@python.org,
 breamore...@yahoo.co.uk says...
 
 C and C++ are weakly and statically typed languages.  Python is a
 strongly and dynamically typed language.  Therefore anything following
 based on the above paragraph alone is wrong.
 
 
 Indeed. I confused strongly/weakly with static. I feel a a bit
 embarrased by it. My apologies.
 
 But no. Nothing that follows from that paragraph is wrong, just because
 of that mistake.
 
 It still stands that list was artifically created to make it look like
 type annotations on top of executable code is a feature of nearly every
 language in the book. When it is not!
 
 Most particularly when it comes to statically typed languages, wich
 Steven didn't feel guilty of including there.

Why should I feel guilty? You wrote:


Static analysis cannot and should not clutter executable code.


But what are type declarations in statically typed languages like C, Pascal,
Haskell, etc.? They are used by the compiler for static analysis. The same
applies to type declarations in dynamically typed languages like Cobra and
Julia. And yet, there they are, in the executable code.

So there are a whole lot of languages, going all the way back to 1950s
languages like Fortran, to some of the newest languages which are only a
few years old like Go, both dynamically typed and statically typed, which
do exactly what you say languages cannot and should not do: they put type
information used for static analysis there in the code.

As I said, these languages disagree with you. You are not just arguing
against Guido, but against the majority of programming language designers
for 60+ years.



-- 
Steven

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


Re: Python is DOOMED! Again!

2015-01-29 Thread Mark Lawrence

On 29/01/2015 18:23, random...@fastmail.us wrote:


Statically typed lanugages by definition can never give you a TypeError
- there are no runtime conversions that can succeed or fail based on the
type of the arguments. What makes a statically typed language strong or
weak? Are statically typed languages always weak?



They can give you lots of warnings about possible loss of data or 
similar.  Yes, you can silence these warnings if you know what you're 
doing.  You can also silence them if you don't know what you're doing. 
The end result could be a lovely, friendly segfault to debug.  What joy?


No, they're not always weakly typed.  The aim of the spreadsheet put up 
by Skip was to sort out (roughly) which languages belong in which camp. 
 I do not regard myself as suitably qualified to fill the thing out. 
Perhaps by now others have?


--
My fellow Pythonistas, ask not what our language can do for you, ask
what you can do for our language.

Mark Lawrence

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


Re: Python is DOOMED! Again!

2015-01-29 Thread Rick Johnson
On Thursday, January 29, 2015 at 10:11:56 AM UTC-6, Steven D'Aprano wrote:

 But what are type declarations in statically typed
 languages like C, Pascal, Haskell, etc.? They are used by
 the compiler for static analysis. The same applies to type
 declarations in dynamically typed languages like Cobra and
 Julia. And yet, there they are, in the executable code.

 So there are a whole lot of languages, going all the way
 back to 1950s languages like Fortran, to some of the
 newest languages which are only a few years old like Go,
 both dynamically typed and statically typed, which do
 exactly what you say languages cannot and should not do:
 they put type information used for static analysis there
 in the code.

 As I said, these languages disagree with you. You are not
 just arguing against Guido, but against the majority of
 programming language designers for 60+ years.

Are we really going to base our design decisions on the same
emotional need to belong that a 14 year girl bases clothing
purchases on? Following your logic, it's high time we adopt
braces, since they have been just as long!

===
 WELCOME TO COMPUTER LANGUAGE JEOPARDY!
===
|--|---|-doot||
|--|---|-do--||
|--|---|---do||
|--dah---dah---|dahdah-|-do--||
|--|---|---do||
|do---doo-do-do|-dodo--|-do--||
|-doo--|---|-|doot|
===
   @2nd skip--- D.C.  

And here is your host: Alex Trebek!

TREBEK: I swear i was not drunk when i sideswiped an array of mailboxes and 
ended up kissing a telephone pole in a ditch 45 feet away... so shut up about 
it already!

[snip: totally scripted introductions]

TREBEK: Okay let's begin!

PLAYER1: Alex, i'll take Language Devolution for 500 please.

TREBEK: Okay, and the answer is: Python Type Hints

(CHIME) PLAYER1: What does a book-licking contest look like?

*WNK*

TREBEK: Sorry, while your answer certainly is a product of such a proposal, 
we're looking for something more specific to the category of: Language 
Devolution.

(CHIME) PLAYER2: Python Insanity Proposal?

*WNK*

TREBEK: Again, technically correct, but your answer must be in the form of a 
question!

(CHIME) PLAYER3: What happens after an emperor forsakes his clothing?

*DING-DING-DING*

TREBEK: Congrats, you're this weeks winner! Please stay tuned for a special 
episode of Keeping Up With The Kewl Kids. Good night folks!

[This episode was sponsored by Alex's AA group]
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Python is DOOMED! Again!

2015-01-29 Thread Chris Kaynor
On Thu, Jan 29, 2015 at 2:57 PM, BartC b...@freeuk.com wrote:
 I've read most of the thread but haven't been able to figure out /why/ this
 is being proposed. What is the advantage, speed?

Check out the rationale part of the PEP:
https://www.python.org/dev/peps/pep-0484/#rationale-and-goals. Other
parts of the PEP also answer many of the other questions you have.
I've put in my understandings and opinions below, however.


There are a few perceived advantages to adding type-hints, some of
which are more pie-in-the-sky while others are very likely:
- IDEs can use them to provide better auto-complete.
- Static checkers can do a better job of checking for program
correctness (by erroring if the type-hints are obviously broken).
- Possibility of performance improvements, although CPython (the
official Python interpreter) will still be required to work if the
type-hints are wrong.

 And how does it work in practice: is it necessary to use a special Python
 interpreter to gain the advantage, or do you only get a benefit after
 putting the source through some sort of compiler system?

CPython will, initially, do nothing special with the type-hints that
it does not currently do with the existing annotations, which is
merely parse them and ensure they meet the syntax requirements. My
understanding is that there will be a static checker (possibly derived
from MyPy) that will be endorsed, however, and possibly included in
the STD.

 I can understand many of the misgivings. I have a dynamic language of my
 own, to which I've tried adding type hinting a few times, but in the end
 decided to get rid of those type hints and keep things purer and simpler
 (and a hell of a lot easier to implement too).

 There was also something unsatisfactory about having to help things along by
 putting in explicit hints, which also cause trouble: what happens when the
 promise you make are wrong? It would mean putting in extra checking code to
 make sure things are what you said.

As a rule, breaking the type-hint promises will have no effect on the
run-time. Some implementations (Cython or PyPy) may decide to use the
type-hints to guide optimization, perhaps by creating a fast path for
if the promises are met, however conforming implementations cannot
rely on them being correct. PyPy, for example, could JIT the function
presuming the types are right, therefore front-loading the JIT time.
I'm not saying they WILL, just that they COULD.

 So when assigning a non-hinted variable to a hinted one, it needs to be
 type-checked, otherwise things can go wrong internally. So far from making
 things faster, it starts by slowing them down!

See above. The current plan is not to generally use the hinting at
run-time, but only during static checking of the code. For cases like
PyPy, they already have to do run-time type checking to ensure
correctness when running optimized paths.

 Putting in hints, (as as I implemented them using primitive types), meant
 that functions and code no longer worked in a generic (or polymorphic)
 manner. Code also changes, but the type hints aren't maintained. I
 understand the Python proposal allows type hints to be a union of expected
 types, but that sounds complicated.

Regarding the maintenance of type-hints, for people who heavily use
them, I would imagine they will have a static checker setup which will
regularly run and generally produce an error if the hints are not
updated. Most other people will likely only lightly use the type-hints
and may not use static checkers, and thus they probably will get out
of sync. With such a feature, my main use case would be to aid IDEs in
providing auto-complete, which I've done in the past by adding lines
like if 0: assert isinstance(variable, type). Most of the functions
I write do not have any such hints but instead I've only generally
used it in cases where the type is pretty much fixed, but is a custom
type with a more complicated API.

For the most part, I would almost never use the union types. From past
experience (from languages like C++ and C#), such tends to vastly over
complicate the definitions. The same applies to most cases of generic
types (think list, tuple, set, dict).

 Depending on how it's implemented, it also seems to me that a program with
 type hints might work with a Python implementation that ignores the hints,
 but might not work with one that makes use of the hints (because of using a
 value that is not of the hinted type).

 Also, how does it work with numeric types; is there just one numeric type,
 or two (integer and real) or more? With more than one numeric type hint,
 it's going to be a headache trying to determine what the result of a
 function can be. (And if you have to choose real because 1% of the time the
 result will be real, then you lose the advantage of being able to work with
 integers 99% of the time.)

My understanding is that you would generally use one of int,
float, Decimal, Rational, complex, etc. - basically the name
of the 

Re: Python is DOOMED! Again!

2015-01-29 Thread BartC

On 22/01/2015 04:30, Steven D'Aprano wrote:


https://www.python.org/dev/peps/pep-0484/



Here's a potential real-world example, from the statistics module in Python
3.4, before and after adding annotations:

def median_grouped(data, interval=1): ...

def median_grouped(data:Iterable[Real], interval:Real=1)-Real: ...



So how does Python's proposed type-hints compared to that used by other
languages?



C:

   double
   median_grouped (IterableOfReal data, double interval)



I think it is clear that Python's annotation syntax remains quite close to
executable pseudo-code. Fears that type-hints will doom Python are not
credible.


I've read most of the thread but haven't been able to figure out /why/ 
this is being proposed. What is the advantage, speed?


And how does it work in practice: is it necessary to use a special 
Python interpreter to gain the advantage, or do you only get a benefit 
after putting the source through some sort of compiler system?


I can understand many of the misgivings. I have a dynamic language of my 
own, to which I've tried adding type hinting a few times, but in the end 
decided to get rid of those type hints and keep things purer and simpler 
(and a hell of a lot easier to implement too).


There was also something unsatisfactory about having to help things 
along by putting in explicit hints, which also cause trouble: what 
happens when the promise you make are wrong? It would mean putting in 
extra checking code to make sure things are what you said.


So when assigning a non-hinted variable to a hinted one, it needs to be 
type-checked, otherwise things can go wrong internally. So far from 
making things faster, it starts by slowing them down!


Putting in hints, (as as I implemented them using primitive types), 
meant that functions and code no longer worked in a generic (or 
polymorphic) manner. Code also changes, but the type hints aren't 
maintained. I understand the Python proposal allows type hints to be a 
union of expected types, but that sounds complicated.


Depending on how it's implemented, it also seems to me that a program 
with type hints might work with a Python implementation that ignores the 
hints, but might not work with one that makes use of the hints (because 
of using a value that is not of the hinted type).


Also, how does it work with numeric types; is there just one numeric 
type, or two (integer and real) or more? With more than one numeric type 
hint, it's going to be a headache trying to determine what the result of 
a function can be. (And if you have to choose real because 1% of the 
time the result will be real, then you lose the advantage of being able 
to work with integers 99% of the time.)


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


Re: Python is DOOMED! Again!

2015-01-29 Thread Chris Angelico
On Fri, Jan 30, 2015 at 9:57 AM, BartC b...@freeuk.com wrote:
 I've read most of the thread but haven't been able to figure out /why/ this
 is being proposed. What is the advantage, speed?

Have a read of the PEP:

https://www.python.org/dev/peps/pep-0484/

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


Re: Python is DOOMED! Again!

2015-01-29 Thread MRAB

On 2015-01-29 23:25, Chris Kaynor wrote:

On Thu, Jan 29, 2015 at 2:57 PM, BartC b...@freeuk.com wrote:

[snip]

Putting in hints, (as as I implemented them using primitive types),
meant that functions and code no longer worked in a generic (or
polymorphic) manner. Code also changes, but the type hints aren't
maintained. I understand the Python proposal allows type hints to
be a union of expected types, but that sounds complicated.


Regarding the maintenance of type-hints, for people who heavily use
them, I would imagine they will have a static checker setup which
will regularly run and generally produce an error if the hints are
not updated. Most other people will likely only lightly use the
type-hints and may not use static checkers, and thus they probably
will get out of sync. With such a feature, my main use case would be
to aid IDEs in providing auto-complete, which I've done in the past
by adding lines like if 0: assert isinstance(variable, type). Most
of the functions I write do not have any such hints but instead
I've only generally used it in cases where the type is pretty much
fixed, but is a custom type with a more complicated API.


I suppose you could check what types the arguments are by running the
code and outputting the types supplied, and then run a script that uses
the info to modify the source, and then you can diff the result.

Here's a simple example I've come up with:


#! python3.4
# -*- coding: utf-8 -*-
import inspect

def check_hints(func):

def wrapper(*args, **kwargs):
sig = inspect.signature(func)
print('file  :', inspect.getfile(func))
print('function  :', func.__name__)
print('line  :', inspect.getsourcelines(func)[1] + 1)

args_given = list(args)
kwargs_given = dict(kwargs)

for name in sig.parameters:
param = sig.parameters[name]
if param.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD:
annotation = param.annotation
if args_given:
print('parameter : {} : {}'.format(name,
  type(args_given.pop(0)).__name__))
if annotation != inspect.Parameter.empty:
print('annotation: {} : {}'.format(name,
  annotation.__name__))
else:
try:
print('parameter : {} : {}'.format(name,
  type(kwargs_given.pop(name)).__name__))
if annotation != inspect.Parameter.empty:
print('annotation: {} : {}'.format(name,
  annotation.__name__))
except KeyError:
pass

result = func(*args, **kwargs)

print('return: {}'.format(type(result).__name__))
annotation = sig.return_annotation
if annotation != inspect.Parameter.empty:
print('annotation: {}'.format(annotation.__name__))

print()

return result

return wrapper

@check_hints
def foo(arg1: int, arg2: str, arg3: float=2.0) - int:
pass

@check_hints
def bar(arg1, arg2, arg3=2.0):
pass

foo(1, bar)
foo(baz, 2, 3.0)

bar(1, bar)
bar(baz, 2, 3.0)
--
https://mail.python.org/mailman/listinfo/python-list


Re: Python is DOOMED! Again!

2015-01-28 Thread Skip Montanaro
On Tue, Jan 27, 2015 at 7:26 PM, Steven D'Aprano 
steve+comp.lang.pyt...@pearwood.info wrote:

 (2) Algol, Ada, Boo, C, C#, C++, Cobol, Cobra, D, F#, Fantom, Fortran, Go,
 Haskell, Java, Julia, Kotlin, Oberon, Pascal, Rust, Scala and dozens
 (hundreds?) of other languages disagree with you.


Well, sure. But that's always been one of the nice things about Python,
less visual clutter. While I understand where all the type hinting activity
is coming from (and accept it as inevitable), I'm also sympathetic to
Mario's perspective.

Python-1.5-anyone?-ly, y'rs,

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


  1   2   3   >