Path: csiph.com!usenet.pasdenom.info!weretis.net!feeder1.news.weretis.net!feeder.erje.net!eu.feeder.erje.net!newsfeed.xs4all.nl!newsfeed2a.news.xs4all.nl!xs4all!post.news.xs4all.nl!not-for-mail Return-Path: X-Original-To: python-list@python.org Delivered-To: python-list@mail.python.org X-Spam-Status: OK 0.000 X-Spam-Evidence: '*H*': 1.00; '*S*': 0.00; 'else:': 0.03; 'subject:Python': 0.06; '"""': 0.07; '(so': 0.07; 'args': 0.07; 'float': 0.07; 'none:': 0.07; 'string': 0.09; 'apis': 0.09; 'arguments': 0.09; 'arguments,': 0.09; 'mixed': 0.09; 'pep': 0.09; 'percentage': 0.09; 'strings.': 0.09; 'toolkit': 0.09; 'type,': 0.09; 'cc:addr:python-list': 0.11; 'def': 0.12; 'gui': 0.12; 'jan': 0.12; 'question.': 0.14; '(note': 0.16; '*args': 0.16; '23,': 0.16; 'args.': 0.16; 'arguments:': 0.16; 'better:': 0.16; 'callable': 0.16; 'callable,': 0.16; 'deletes': 0.16; 'equivalents': 0.16; 'factory': 0.16; 'flag).': 0.16; 'from:addr:rosuav': 0.16; 'from:name:chris angelico': 0.16; 'hell.': 0.16; 'int)': 0.16; 'integer.': 0.16; 'iterable,': 0.16; 'meanwhile': 0.16; 'name),': 0.16; 'objects.': 0.16; 'optional': 0.16; 'reasonable.': 0.16; 'sane': 0.16; 'simple.': 0.16; 'specifying': 0.16; 'syntactic': 0.16; 'usd,': 0.16; 'followed': 0.16; 'wrote:': 0.18; 'passing': 0.19; 'typing': 0.19; '(the': 0.22; 'otherwise,': 0.22; 'cc:addr:python.org': 0.22; 'case.': 0.24; 'instance,': 0.24; 'integer': 0.24; 'replace': 0.24; 'specify': 0.24; 'string,': 0.24; '(or': 0.24; 'question': 0.24; 'cc:2**0': 0.24; 'equivalent': 0.26; 'this:': 0.26; 'pass': 0.26; 'values': 0.27; 'gets': 0.27; 'header:In-Reply-To:1': 0.27; 'idea': 0.28; 'function': 0.29; 'am,': 0.29; 'array': 0.29; 'raise': 0.29; 'said,': 0.30; 'message-id:@mail.gmail.com': 0.30; 'gives': 0.31; 'getting': 0.31; 'away.': 0.31; 'consequence': 0.31; "d'aprano": 0.31; 'fault': 0.31; 'steven': 0.31; "they'll": 0.31; 'void': 0.31; 'anyone': 0.31; 'class': 0.32; 'figure': 0.32; 'stuff': 0.32; 'quite': 0.32; 'fri,': 0.33; 'guess': 0.33; 'there,': 0.34; 'maybe': 0.34; 'could': 0.34; 'problem': 0.35; "can't": 0.35; 'common': 0.35; 'possible.': 0.35; '(2)': 0.35; 'one,': 0.35; 'but': 0.35; 'received:google.com': 0.35; 'really': 0.36; 'object,': 0.36; 'sequence': 0.36; 'shorter': 0.36; 'possible': 0.36; 'should': 0.36; 'employee': 0.37; 'wrong': 0.37; 'too': 0.37; 'implement': 0.38; 'skip:o 20': 0.38; '(3)': 0.38; 'skip:[ 10': 0.38; 'does': 0.39; 'bad': 0.39; 'itself': 0.39; 'though,': 0.39; 'either': 0.39; 'how': 0.40; 'even': 0.60; 'skip:u 10': 0.60; 'length': 0.61; 'simply': 0.61; "you're": 0.61; 'guarantee': 0.63; 'more': 0.64; 'different': 0.65; 'natural': 0.68; 'useful.': 0.68; 'union': 0.69; 'money': 0.72; 'subject:! ': 0.74; '"just': 0.84; '2015': 0.84; 'complexity': 0.84; 'float,': 0.84; 'imagine,': 0.84; 'pike': 0.84; 'think:': 0.84; 'union,': 0.84; 'to:none': 0.92 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:in-reply-to:references:date:message-id:subject:from:cc :content-type; bh=rM8wlymwzN5flh0gIoibJL9nmccza30YCq4JRTBTLWs=; b=tvR8TBqHTt/ziFZFeF3KBsFBD1VoINtoGKVAsNHVshIqQr3DOmx23mdus3ZESdK5je hT20trgim2m4YXDz2/uLs0S19MBvD6/RW2S3SyOMVBgE1CO9wutfLVSArPFJ7vIiudTT XSASiL0uV+sEKB3hxH/JpdkqiFyxc4AjQn7FRmIeXtUgAC3NKTKDvRSwNfSY7BrW2i87 sLdMqEgnGTVccI7fiaYUvMl3N8r6MJ9QRUH8LlxfOWQCnemhaeqs6SFBzU1R8g31YZxf AzBrKaYoJ4mfXk37/6ADBaHumPMJf8V7sFkNnrzAEsxG9N6zxL3+AkQ8AnsPG30QH+s5 cA2Q== MIME-Version: 1.0 X-Received: by 10.140.90.112 with SMTP id w103mr3083332qgd.65.1421939500541; Thu, 22 Jan 2015 07:11:40 -0800 (PST) In-Reply-To: <54c1063c$0$13008$c3e8da3$5496439d@news.astraweb.com> References: <54c07d04$0$13012$c3e8da3$5496439d@news.astraweb.com> <54c0a571$0$13002$c3e8da3$5496439d@news.astraweb.com> <54c1063c$0$13008$c3e8da3$5496439d@news.astraweb.com> Date: Fri, 23 Jan 2015 02:11:40 +1100 Subject: Re: Python is DOOMED! Again! From: Chris Angelico Cc: "python-list@python.org" Content-Type: text/plain; charset=UTF-8 X-BeenThere: python-list@python.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: General discussion list for the Python programming language List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Newsgroups: comp.lang.python Message-ID: Lines: 125 NNTP-Posting-Host: 2001:888:2000:d::a6 X-Trace: 1421939508 news.xs4all.nl 2958 [2001:888:2000:d::a6]:46473 X-Complaints-To: abuse@xs4all.nl Xref: csiph.com comp.lang.python:84233 On Fri, Jan 23, 2015 at 1:16 AM, Steven D'Aprano wrote: > Mario Figueiredo wrote: > >> def handle_employees(emp: Union[Employee, Sequence[Employee]], raise: >> Union[float, Sequence[float]]) -> Union[Employee, Sequence[Employee], >> None]: > > Using > floats for money is Just Wrong and anyone who does so should have their > licence to program taken away. But using a float to specify a percentage raise that an employee (or class of employees) gets would be reasonable. You could "handle" all your problem employees by giving them an across-the-board 3% raise to try to stop them from going on strike (they have a Union, as we can see there, so strikes are clearly possible). Not that that matters to the typing question. If it really is a monetary type, all you need to do is replace 'float' with 'Money' which would be a superclass to USD, AUD, GBP, and RepublicCredits. There's still the question of what it's doing. My best guess about this function is that it has three modes: 1) handle_employee(emp: Employee, pay_raise: float) Give one employee a percentage raise. (Note the different function name.) 2) handle_employees(emp: Sequence[Employee], pay_raise: float) Give all these employees the same percentage raise. 3) handle_employees(emp: Sequence[Employee], pay_raise: Sequence[float]) Equivalent to [handle_employee(e, r) for e, r in zip(emp, pay_raise)] I can't figure out any sane meaning for passing a single employee and a sequence of floats, and I think the one-and-one case should have a different name. That would mean that there's really only one Union involved, and the plural function would look like this: def handle_employees(emp: Sequence[Employee], pay_raise: Union[float, Sequence[float]]): """Atomically give many employees raises. If pay_raise is a float, gives each employee that raise; otherwise, it should be a sequence of the same length as emp. """ if isinstance(pay_raise, collections.Sequence): for e, r in zip(emp, pay_raise): handle_employee(e, r) else: for e in emp: handle_employee(e, pay_raise) But I still have no clue what the return value of either the one-and-one case or the multiple case should be. > (3) Have a shorter way to declare "Spam or Sequence (tuple?) of Spam". > > > def handle_employees( > emp: OneOrMore[Employee], > pay_raise: OneOrMore[int]) > -> OneOrMore[Employee] | None: > pass Yes, that would be a reasonable thing. It might even be possible to implement it on top of TypeVar. That said, though, APIs that accept "just one, or maybe more" have problems; for instance, you can't write a "pretty-print" function that can take either an arbitrary object, or a sequence of arbitrary objects. Fundamentally not possible. But when you can guarantee that the thing you're getting OneOrMore of is not itself iterable, it would be useful. >> Meanwhile there's quite a few more generics like the Sequence one above >> you may want to take a look at and try and remember. And that's just one >> factory (the generics support factory). You may also want to take a look >> at TypeVar and Callable for more syntactic hell. > > Exaggerate, much? Maybe. Callable does get pretty hairy; specifying the arguments and return values of a function is never pretty. Pike is no better: > typeof(write); (1) Result: function(array(string) | string, mixed ... : int) That's a function which takes either a single string or an array of strings, followed by any number of additional arguments, and returns an integer. > typeof(GTK2.setup_gtk); (2) Result: function(void | array(string) | string, void | int : array(string)) Optional arguments: one or more strings, and maybe an integer. Returns an array of strings. > typeof(rm); (3) Result: function(string : int) This one's pretty simple. It takes a string (path name), and returns an integer (success or failure flag). And deletes a file/directory. > typeof(asin); (4) Result: function(int | float : float) Could take an integer or a float, and returns a float. The PEP 484 equivalents would be, I think: write = Callable[[Union[str, Sequence[str]], AnyArgs], int] # I have no idea how to do optional args. rm = Callable[[str], int] asin = Callable[[Union[int, float]], float] It's not too bad when the function signature is really simple. And that's going to be the common case. But it is syntactically complicated to type-check a callback's arguments. Imagine, for instance, GUI toolkit callbacks; chances are you can stuff extra args into them (so they need a *args equivalent at the end), and they'll take a variety of different args. They will not be pretty... or else they'll be type-hinted simply as Callable, with nothing else. But ultimately, that's not a fault of the type hinting structure. It's a natural consequence of the complexity of callbacks. You can't get away from it. ChrisA