Lisp examples are quoted directly from Practical Common Lisp
Let's refresh our knowledge of WinAPI - the dearly beloved API of all Windows developers. For instance, ShellExecute:
HINSTANCE ShellExecute(
HWND hwnd,
LPCTSTR lpOperation,
LPCTSTR lpFile,
LPCTSTR lpParameters,
LPCTSTR lpDirectory,
INT nShowCmd
);
On the whole I think I remember which parameters this function requires and why. Not too clearly though. I do remember the first three. The fourth might spring to mind while writing the function. The last too however, are quire elusive and I'm never quite sure which goes where and when. The problem gets worse if you remember that som parameters, hwnd, lpParameters, lpDirectory in our case, are optional and can be replaced with NULLs. So, we would quite often invoke this function like this::hResult = ShellExecute(NULL, "open", "path/to/file", NULL, NULL, SW_SHOWDEFAULT);Scary, huh? Now imagine if we could write this function like this:
hResult = ShellExecute(operation="open", file="path/to/file");Beautiful. Now, this is what named parameters are all about.
Let's go back to Lisp. Let's consider the following function:
(defun foo (&key a b c) (list a b c))
This function accepts three parameters and simply listst them in their order. This is how this function behaves when we invoke it in numerous ways:
(foo) ==> (NIL NIL NIL) (foo :a 1) ==> (1 NIL NIL) (foo :b 1) ==> (NIL 1 NIL) (foo :c 1) ==> (NIL NIL 1) (foo :a 1 :c 3) ==> (1 NIL 3) (foo :a 1 :b 2 :c 3) ==> (1 2 3) (foo :a 1 :c 3 :b 2) ==> (1 2 3)
Note the last line. Even though the order or parameters is incorrect, the function still behaves as designed. Now, that's the beauty and power of named parameters. A developer now only needs to know which parameters a function requires, not their not their order.
Unfortunately none of the mainstream languages support named parameters, not natively at least. That is why invoking a function (especially a WinApi function) is a long and tedious process whereas we could simply make do with passing a limited number of named arguments (as my example with ShellExecute shows).
I am currently developing in ColdFusion and it supports named parameters natively (though you cannot honestly call it a mainstream language):
<cffunction name="function" access="public">
<cfargument name="arg1" type="numeric" required="yes">
<cfargument name="arg2" type="string" required="no">
<!--- и так далее --->
</cffunction>
This function is invoked as follows:<cfset function(arg1=1, arg2='string')>As in Lisp, the order of arguments is not important as long as they are named.
Some languages (scripting languages, mostly) emulate this behaviour through the use of hashes. PHP anyone?
function a_func($params)
{
if(isset($params['arg1'])) /* do stuff */
if(isset($params['arg2'])) /* do stuff */
}
/* Calling the function */
$result = a_func(array('arg1' => 123, 'arg2' => 'a string'));
That is, we actually pass an array, but it serves our purpose well enough. Ruby employs exactly the same technique:
# find from ActiveRecord
def find(*args)
options = extract_options_from_args!(args)
validate_find_options(options)
set_readonly_option!(options)
case args.first
when :first then find_initial(options)
when :all then find_every(options)
else find_from_ids(args, options)
end
end
# Calling the function:
Person.find(:first, :order => "created_on DESC", :offset => 5)
Person.find(:all, :group => "category")
Person.find(:all, :offset => 10, :limit => 10)
# where Person is an object of type Activerecord
However, none of these can even compare to Lisp's implementation. ColdFusion, for instance, doesn't accept an arbitrary number of parameters (see Lisp's &rest). Hashes both in PHP and Ruby are rather cumbersome because implementation of the function may become convoluted (or you have to resort to using less-than-safe extract or extract_options_from_args! functions). I'm not even talking other languages like С/С++, C# and Java :). Some languages though, like Haskell, Erlang and Nemerle, have pattern matching allows the developer to work arounв named parameters in a very elegant manner, but that is a story for another day.