The Beauty of Perl 6 Parameter Passing
by Phil Crow
|
Pages: 1, 2
Assigning Defaults
Adding genuine, compiler-enforced parameters to sub declarations is a giant leap forward for Perl. For many people, that particular looseness in Perl 5 keeps it out of any discussions about what language to use for a project. I experienced this unfortunate reality firsthand in my last job. There's a lot more to declarations in Perl 6, though.
Suppose I want to give the caller control over the accuracy of the method, yet I want to provide a sensible default if that caller doesn't want to think of a good one. I might write:
package Newton;
use v6;
sub newton(
Num $target,
Num :$epsilon = 0.005, # note the colon
Bool :$verbose = 0,
) is export {
my Num $guess = $target;
while (abs( $guess**2 - $target ) > $epsilon ) {
$guess += ( $target - $guess**2 ) / ( 2 * $guess );
say $guess if $verbose;
}
return $guess;
}
Here I've introduced two new optional parameters: $verbose, for whether to print at each step (the default is to keep quiet) and $epsilon, the Greek letter we math types often use for tolerances.
While the caller might use this exactly as before, she now has options. She might say:
my $answer = newton(165, verbose => 1, epsilon => .00005);
This gives extra accuracy and prints the values at each iteration (which prints the value of the last iteration twice: once in the loop and again in the driving script). Note that the named parameters may appear in any order.
Making Assumptions
Finally, Newton's method can find roots for more things than just squares. To make this general requires a bit more work and some extra math (which I'll again brush under the rug).
It is easy enough to supply the function for which you want roots. For example, the squaring function could be:
sub f(Num $x) { $x**2 }
Then, in the update line of the loop, write:
$guess += ( $target - f($guess) ) / ( 2 * $guess );
Changing f would change the roots you seek.
The problem is on the far side of the division symbol. 2 * $guess depends on the function (it's the first derivative, for those who care). I could require the caller to provide this, as in:
sub fprime(Num $x) { 2 * $x }
Then the update would be:
$guess += ( $target - f($guess) ) / fprime($guess);
There are two problems with this approach. First, you need a way for the caller to pass those functions into the sub. That's actually pretty easy; just add parameters of type Code to the list:
sub newton(
Num $target,
Code $f,
Code $fprime,
Num :$epsilon = 0.005,
Bool :$verbose = 0,
) is export {
The second problem is that the caller may not know how to calculate $fprime. Perhaps I should make calculus a prerequisite for using the module, but that just might scare away a few potential users. I want to provide a default, but the default depends on what the function is. If I knew what $f was, I could estimate $fprime for users.
Perl 6 provides precisely this ability. Here's the final module, a bit at a time:
package Newton;
use v6;
That's nothing new.
sub approxfprime(Code $f, Num $x) {
my Num $delta = 0.1;
return ($f($x + $delta) - $f($x - $delta))/(2 * $delta);
}
For those who care (surely at least one person does), this is a second-order centered difference. For those who don't, its an approximation suitable for use in the newton sub. It takes a function and a number and returns an estimate of the value needed for division.
sub newton(
Num $target,
Code $f,
Code :$fprime = &approxfprime.assuming( f => $f ),
Num :$epsilon = 0.0005,
Bool :$verbose = 0,
) returns Num is export {
my Num $guess = $target / 2;
while (abs($f($guess) - $target) > $epsilon) {
$guess += ($target - $f($guess)) / $fprime($guess);
say $guess if $verbose;
}
return $guess;
}
A script using this program could be as simple as:
#!/usr/bin/pugs
use Newton;
sub f(Num $x) { return $x**3 }
say "{ newton(8, \&f, verbose => 1, epsilon => .00005) }";
Note that the caller must supply the function f. The one in the example is for cube roots.
If the caller provides the derivative as fprime, I use it. Otherwise, as in the example, I use approxfprime. Whereas a caller-supplied fprime would take one number and return another, approxfprime needs a number and a function. The function needed is the one the caller passed to newton. How do you pass it on? Currying—that is, supplying one or more of the parameters of a function once, then using the simplified version after that.
In Perl 6, you can obtain a reference to a sub by placing the sub sigil & in front of the function's name (providing it is in scope). To curry, add .assuming to the end of that and supply values for one or more arguments in parentheses. All of this is harder to talk about than to do:
Code :$fprime = &approxfprime.assuming( f => $f ),
This code means that the caller might supply a value. If this is the case, use it. Otherwise, use approxfprime with the caller's function in place of f.
Conclusion
Perl 6 calling conventions are extremely well designed. Not only do they allow compile-time parameter checking, they also allow named parameters with or without complex defaults, even including curried default functions. This is going to be very powerful. In fact, with Pugs, it already is.
There is a slightly more detailed version of the example from this article in the examples/algorithms/ directory of the Pugs distribution. It's called Newton.pm.
Disclaimer
As much as it pains me to say it, if you need heavy duty numerics, don't code in pure Perl. Rather, use FORTRAN, C, or Perl with PDL. And be careful. Numerics is full of unexpected gotchas, which lead to poor performance or outright incorrect results. Unfortunately, Newton's method, in the general case, is notoriously risky. When in doubt about numerics, do as I do and consult a professional in the field.
You must be logged in to the O'Reilly Network to post a talkback.
Showing messages 1 through 15 of 15.
- Questions...
2007-03-05 13:52:35 dextius [Reply]
1. The author says "I can use this syntax in Perl 5." in reference to assigning types to variables. I've done this with objects, but never with simple scalars (and specifically the "Num" prefix). Did I misunderstand something?
2. Will perl6 contextually adapt the value of a scalar like perl5 does (if it isn't typed? or, heck, even if it is?)
3. What are the colons for in the parameter list. I initially thought they meant optional parameters, but you'd think that the "= value" would sort of imply that..
4. I really dislike python, as a whole, but really like their method signatures. Perl6's approach to this seems, well, not as nice :( ... (umm, not a question). Which exegesis, apocalypse etc. covers this notion in more detail, so I can get a better grip if this going to suck or not?
5. "say" looks really neat, but wow, defaulting a hard return in the output?! Doesn't sound like a perl-ish function call to me! :( Is this prevalent throughout the language?
6. Are ampersands a required sigil for subroutine calls now? Just curious, I always thought they should for consistancies sake (not just to ignore the prototype of the function)..- Questions...
2007-03-07 19:08:13 chromatic1 [Reply]
Good questions. I ran them by Larry, who responded:
1. Phil's answer to this is adequate, though it leaves out the
notion of constraints, and types that aren't classes.
2. Perl will keep any value's type consistent with the declared constraints
on its container. The basic types still autocoerce as in Perl 5, but
other types do not autocoerce. Either you use an explicit coercion naming
the type, or write your multis explicitly to allow for autocoercion.
3. Phil's answer is a little sideways here. The leading colon indicates a
named-only parameter, which happens to be optional. But optional positions are
indicated with either trailing "?" or "= value".
4. Perl 6 signatures are much more powerful pattern matchers, especially
when matching against their counterparts known as captures. The simple cases
remain simple and sane, however.
5. I suppose you could point out that we debated this one quite a bit.
6. In Perl 6, the & is used only when you need to refer to a routine
as a noun where it might otherwise be misconstrued as a verb. The consistency
we're looking for here is not that all identifiers have sigils, but that all
nouns have sigils.- Questions...
2007-03-08 15:31:20 philcrow [Reply]
Thanks for clarifying and correcting my off the cuff answers.
- Questions...
- Questions...
2007-03-05 15:56:16 philcrow [Reply]
1. The author says "I can use this syntax in Perl 5." in reference to assigning types to variables. I've done this with objects, but never with simple scalars (and specifically the "Num" prefix). Did I misunderstand something?
The types do need to be classes in Perl 5. In Perl 6 the basic types have classes.
2. Will perl6 contextually adapt the value of a scalar like perl5 does (if it isn't typed? or, heck, even if it is?)
I have to defer to the language designers on that one. But, I think the answer is something like this. If the variable is used in an expression, it will be coerced as in Perl 5. If you are passing it to a sub or method with a required type it must be of that type (unless you relax the compilation rule in some way I don't know about).
3. What are the colons for in the parameter list. I initially thought they meant optional parameters, but you'd think that the "= value" would sort of imply that..
The colons do mean optional. You don't have to provide default for optional values. So, you don't always need the equal sign (then you would get undef if the optional param is omitted).
4. I really dislike python, as a whole, but really like their method signatures. Perl6's approach to this seems, well, not as nice :( ... (umm, not a question). Which exegesis, apocalypse etc. covers this notion in more detail, so I can get a better grip if this going to suck or not?
Read Synopsis 6 for more information. http://feather.perl6.nl/syn/ has a list of all the synopses, which are now defnitive and kept reasonably up to date.
5. "say" looks really neat, but wow, defaulting a hard return in the output?! Doesn't sound like a perl-ish function call to me! :( Is this prevalent throughout the language?
The neat features used with say in the article are really features of double quoted strings in any context. say is just a perl 6 synonym for print which adds a trailing new line. If you don't like it, keep using print, but with the new features.
6. Are ampersands a required sigil for subroutine calls now? Just curious, I always thought they should for consistancies sake (not just to ignore the prototype of the function)..
I don't think you need the sub sigil to call the method or function. But, as in perl 5, sometimes you need them when you need a reference to a sub.
Phil- Questions...
2007-05-12 02:43:30 Aankhen [Reply]
"The colons do mean optional. You don't have to provide default for optional values. So, you don't always need the equal sign (then you would get undef if the optional param is omitted)."
Just one correction: optional parameters are marked by a default value and/or a trailing question mark: sub foo ($bar?, $baz = "default", $quux? = "second default")
A colon, on the other hand, indicates a named-only parameter.
- Questions...
2007-03-05 23:32:30 jczeus [Reply]
> I don't think you need the sub sigil to call the method or function. But, as in perl 5, sometimes you need them when you need a reference to a sub.
It's even forbidden to use the ampersand when calling a sub in Perl6. You only use it when you refer the sub per se. OTOH, you don't need the backslash anymore, for example when you use &f as an argument to newton():
sub f( Num $x ) { return $x**3 }
sub fprime( Num $x ) { return $x**2 * 3 }
say "{ newton( 8, &f, fprime => &fprime, verbose => 1, epsilon => .00005 ) }";
Great article, BTW! This really shows the beauty of Perl6-style parameter handling. "Beauty" is of course subjective. I really hope more people will discover this for themselves, instead of posting "humourous" comments in the vein of "ugh, this contains lots of symbols I don't know from C. This is ugly."
- Questions...
- Questions...
- Sic transit gloria mundi
2007-03-05 04:36:49 1none1 [Reply]
Simply and elegant perl5 parameters passing will be replace by new complex and ugly mechanism. Perl6 seems a typical language designed by committee.- Sic transit gloria mundi
2007-03-05 11:09:43 chromatic1 [Reply]
You can still use the old conventions if you want, but if you want more power (multi-dispatch is nice), the new conventions are available. I used some of them when Porting Test::Builder to Perl 6 (http://www.perl.com/pub/a/2005/07/28/test_builder_p6.html) to good effect.- Sic transit gloria mundi
2007-03-06 01:04:19 1none1 [Reply]
Yes, I can, but I am not living in ivory tower - anyway I must use another people's code.- Sic transit gloria mundi
2007-03-06 20:59:23 pgunn [Reply]
You may have to deal with other people's code, but named parameters in Perl5 are things some people use already, and it's a lot clumsier (and less powerful) to have them outside the prototype.
- Sic transit gloria mundi
- Sic transit gloria mundi
- Sic transit gloria mundi
- Yes, more please.
2007-03-01 15:46:07 wruppert [Reply]
Those of us who tired years ago of following Perl 6 progress need lots of articles like this to get us up to speed, since the thing is threatening to actually ship someday.
- How quaint...
2007-03-01 14:17:08 wneumann [Reply]
So Perl 6 is finally getting labeled/optional arguments and type assertions? Isn't that cute... It's almost like it's the 1990s again.- How quaint...
2007-03-03 06:31:16 lichtkind [Reply]
perl6 signatures are able to do much more than the ones in the 90ties. they also work like contracts, for multi methods and much more.- How quaint...
2007-03-04 18:39:42 sigzero [Reply]
He fears what he doesn't understand...so he lashes out.
- How quaint...
- How quaint...



