Re: Python is DOOMED! Again!
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!
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!
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!
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!
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!
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!)
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!)
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!)
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!)
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!)
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!)
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!)
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!)
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!)
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!)
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!)
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!)
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!)
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!)
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!)
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!)
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!)
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!)
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!)
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!)
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!)
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!)
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!)
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!)
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!)
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!
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!)
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!)
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!)
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!)
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!)
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!)
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!)
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!)
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!)
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!
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!)
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!
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!
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!
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!
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!)
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!
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!
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!)
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!)
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!
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!)
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!)
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!
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!)
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!)
-- 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!
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!
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!
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!)
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!
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!
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!
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!
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!)
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!
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!
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!)
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!
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!
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!
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!
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!
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!
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!
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!)
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!
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!
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!
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!
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!
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!
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!
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!
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!
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!
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!
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!
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!
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!
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!
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!
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!
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!
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!
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!
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!
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!
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