Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:47750 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 50074 invoked from network); 2 Apr 2010 21:18:00 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 2 Apr 2010 21:18:00 -0000 Authentication-Results: pb1.pair.com smtp.mail=gregory@gregory.net; spf=permerror; sender-id=unknown Authentication-Results: pb1.pair.com header.from=gregory@gregory.net; sender-id=unknown Received-SPF: error (pb1.pair.com: domain gregory.net from 216.40.44.8 cause and error) X-PHP-List-Original-Sender: gregory@gregory.net X-Host-Fingerprint: 216.40.44.8 smtprelay0008.hostedemail.com Received: from [216.40.44.8] ([216.40.44.8:55611] helo=smtprelay.hostedemail.com) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id 75/82-44965-60F56BB4 for ; Fri, 02 Apr 2010 16:17:59 -0500 Received: from filter.hostedemail.com (ff-bigip1 [10.5.19.254]) by smtprelay02.hostedemail.com (Postfix) with SMTP id DF82F2033686 for ; Fri, 2 Apr 2010 21:17:55 +0000 (UTC) X-Panda: scanned! X-Spam-Summary: 2,0,0,ac553a6bb1a34721,d41d8cd98f00b204,gregory@gregory.net,internals@lists.php.net,RULES_HIT:1:2:355:379:541:564:945:947:973:982:988:989:1260:1277:1311:1313:1314:1345:1437:1515:1516:1518:1593:1594:1605:1730:1747:1766:1792:2107:2194:2198:2199:2200:2378:2379:2393:2553:2559:2562:2693:2731:2740:2890:2898:2903:3653:3865:3866:3867:3868:3869:3870:3871:3872:3873:3874:4031:4037:4042:4044:4052:4250:4365:4470:5007:6119:6261:7594:7875:7903:7904:8660:8700:8957:9010:10004,0,RBL:none,CacheIP:none,Bayesian:0.5,0.5,0.5,Netcheck:none,DomainCache:0,MSF:not bulk,SPF:fn,MSBL:none,DNSBL:none,Custom_rules:0:0:0 X-Session-Marker: 677265676F727940677265676F72792E6E6574 X-Filterd-Recvd-Size: 10480 Received: from nyc-macbookpro.emg.dev (unknown [63.74.178.82]) (Authenticated sender: gregory@gregory.net) by omf02.hostedemail.com (Postfix) with ESMTP for ; Fri, 2 Apr 2010 21:17:55 +0000 (UTC) Message-ID: <745C5243-EB51-4D43-B036-8A34CDBBB547@gregory.net> To: internals@lists.php.net Content-Type: text/plain; charset=US-ASCII; format=flowed; delsp=yes Content-Transfer-Encoding: 7bit Mime-Version: 1.0 (Apple Message framework v936) Date: Fri, 2 Apr 2010 17:17:54 -0400 X-Mailer: Apple Mail (2.936) Subject: Named Parameters From: gregory@gregory.net (GM) I was hoping to start some discussion regarding named parameters. I'm sorry if it's frowned upon to re-post or re-paste something, but I'm thinking that perhaps my email (with the subject Re: Hi) was overlooked on the list in whatever readers, so I'm going to post it again, this time with a better subject. My apologies if this is frowned upon, I'm just trying to bring up a discussion about this issue I'd like to see in PHP 6. Once again I'd love to create an RFC for this, but I don't think I have permissions on the wiki to do that. What do I do to get those privileges granted to my wiki account? ==== original ==== ... I've been a PHP developer for a long time (and before that, C / C++ ) and as I saw code grow and change, I developed design patterns to help ensure maintainability in the long term. In fact, as a founder of a business (and not just a programmer), I value the readability and maintainability of code even more than things like elegance, expressiveness, or programmer talent, because it scales better and is more robust, letting programmers get up to speed. Anyway I noticed that the following always seems to happen over time: in any project, a set of functions and classes is created, which may or may not become a reusable library. There is code that calls these functions. Over time, the classes get more methods, and the functions may be overloaded to handle more functionality. The classes are designed to expand nicely, and nothing needs to be done. But the functions start looking pretty bad after a few years, because of the parameter mechanism. By necessity, the order of the parameters corresponds to the time at which they are introduced, and not something more logical. Providing defaults for the parameters does very little to mitigate this. Experience has shown that hardly anyone ever predicts at the outset how a library will evolve, and currently function "interfaces" are something that needs to be planned carefully at the start, or else can devolve into some nonsense that requires calls like: myFunc(3, 4, null, 4, 8, null, "blah") Besides being hard to follow, it's also cryptic to the reader of the call. We are not sure what the null or 4 corresponds to, without looking up the function definition (perhaps in a tooltip in an IDE). The solution is simple. I've gotten into a habit of defining almost all my functions like this: function myFunc($required1, $required2, array $optional = array()) { } As the function's interface evolves over time, new required parameters cannot be introduced (as it would break the old calls to the function, which may be in the wild). However, functionality can be extended via optional parameters. Also, the calls to the function always look much cleaner: myFunc(3, 4, array('clean' => true, 'name' => 'title')) Inside the actual function implementation, I may very well just do: extract($optional). The $optional array can override existing parameters, etc... in fact, I can very easily set some defaults, with either one of these two patterns: $clean = true; $name = 'default'; extract($optional); or the javascript-like way: $defaults = array('clean' => true, 'name' => 'default'); extract(array_merge($defaults, $optional)); So why am I saying all of this? Well, PHP up until 4 had a reputation for lots of bad coding style, spaghetti code, etc. With PHP 5 and better OOP, this reputation has lessened, especially with the great libraries out there (Zend Framework, Kohana, symfony, etc.) But there is still a problem. We (or at least I) would want PHP 6's reputation to be even better, and the reputation is largely based on how maintainable and cost-effective the code is, which is written by programmers actually using PHP. Blaming newbie programmers is one thing, and may even have some truth to it (PHP is simple - thus attracts newbies) but it's always good for a language to steer you in the right direction by default, and make it easier to write maintainable code. PHP has a great opportunity to do this, by implementing a very simple change, turning all current PHP 5 code, and all code that will be written in the future, into maintainable code. Moreover, it won't break any existing code, like some other additions to PHP 6 might do. This one simple tweak will make PHP 6 code much more reusable and maintainable, just like namespace support will potentially make future PHP code more interoperable without having to write Artificial_Namespace_Class, like ZF and PEAR do. So here is my proposal. Let's have named parameters. Yes, I know this has been brought up before and rejected -- although I don't know what the discussion was, so I would like to have one in light of what I said above. Other languages have elegant solutions to this ... python, javascript, etc. and yes I realize that a common response is "PHP is PHP and not those other languages". But this might be a short-sighted response, as other languages may have winning features that really make it possible for programmers to write better code, and develop better coding and thinking patterns, at no cost, and we would do well to consider whether PHP could benefit from a feature like that. I propose we do two things in PHP 6: 1) Extend function call syntax and semantics so that providing parameters to a function has exactly the same syntax as creating an array. Namely: myFunc(3, 4, 'a' => 'b', 'c' => 'd') would be possible, just like array(3, 4, 'a' => 'b', 'c' => 'd') COMPATIBILITY: This shouldn't break any userland code in earlier versions of PHP, because current function calls are simply a subset of the proposed functionality. They will continue to work in exactly the same way, from the point of view of userland. PROS: a) Makes understanding the language easier for everyone, since now there is only one thing to remember rather than two (current docs are a bit hard to grasp regarding references). Two concepts are unified into one, reducing mind clutter. Passing parameters becomes like creating an array, and references are handled nicely within this unified paradigm. b) Most importantly, makes EVERY function and method extensible as I described above. No more adding parameters ad-hoc in the order they are dreamed up. Instead, they can be added as named parameters, just like the $optional array I was using above. While programmers can choose to use my design pattern, those that didn't plan out all future versions of the function (let's face it, the majority) still get to reap the advantage of this design pattern, simply via named parameters. Leading to *vastly more maintainable code*, which is what I am really advocating here. c) Calls to functions will no longer be cryptic, but actually descriptive. Compare myFunc(3, 4, null, null, array(), 5) to myFunc(3, 4, 'max' => 5) CONS: There is only one I can think of. Named parameters are superior to parameters with default values (foo = "bar") in every way except one: because the defaults are assigned within the function implementation, they have to be documented separately (in DocBlocks, for example). I had someone bring up the objection that code using this design pattern would be less self-documenting. My response is that a) with magic methods, PHP hardly encourages self documenting code anyway, and b) the actual maintainability gains that result from sensible function interfaces will far outweigh whatever negligible benefit one may get from knowing parameter defaults from function signatures. If anything, it'll cause more people to document defaults properly, using DocBlocks or another method. 2) Extend func_get_args to be able to return associative arrays. ANALYSIS: This shouldn't break existing function calls, and here's why. Old versions of the function won't use this feature, so function calls to them won't need to change, either. New versions of the function will be written after this feature is introduced (i.e. in PHP6), so will be aware of the possibility of named parameters. Old calls to new versions of the function simply won't use the feature, so they won't need to change, either. New calls to new versions of the function have the option of using the feature, or not. So nothing is broken at all, but new functionality is there for you to use in PHP6. PROS: a) This once again makes PHP more elegant and unified. One of PHP's most notorious / cool features is that it lets you use array() for both numerically indexed arrays and associative arrays (a.k.a. maps, hashes, dictionaries, etc.) Why not weave this into PHP function calls as well? PHP is so function oriented. And this would give developers a lot of expressive power while at the same time promoting better code and the maintainability we talked about. I personally feel it's purely awesome whenever an extension is able to achieve both of those. b) Lots of PHP programmers also work with Javascript. This would make them happy -- they can use what they consider to be a very useful coding pattern in JS (jQuery and most other popular frameworks use it), and start doing it in PHP: function myFunc($a, $b) { $id = uniqid(); $title = 'People hi'; extract(func_get_args()); // possibly overriding the parameters } CONS: a) Maybe there are some internal problems to implementing this, so serious that it's infeasible? b) Will this break any PECL extensions? If so, are we planning to maintain compatibility with all PECL extensions written for earlier versions of PHP? Is it worth it to introduce this feature? What do you guys think? I really want PHP 6 to rock and have an even better reputation among businesses, programmers, etc. Sincerely, Greg Magarshak