Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW

TIMTOWTDI, synergy

by John M. Dlugosz (Monsignor)
on Jun 02, 2009 at 18:49 UTC ( #767727=note: print w/replies, xml ) Need Help??

in reply to How A Function Becomes Higher Order

Reading this thread (years after it was started), I reflected on the differences in approaches advocated in the replies. OO, with a derived class used to specialize on the type involved, is certainly the way to go if the objects will be persistent, or if a number of functions are created that work together. I quickly change my design from a callback function to a callback object instead of N callback functions.

It is also very familiar to people from more ordinary experience, and in many languages it is really the only choice.

You can also use a class with a virtual function to substitute for a closure if that's what you really wanted. In languages where I wish for the latter, I see that it is indeed a substitution.

In the case discussed about specializing the algorithm (or object) to compare different kinds of objects in a type-suitable manner, I think of overloading. Virtual functions give you that, but actual overloading of the desired function would be easier. In particular, the max function should know what to do for any kind of argument. In many languages, that's fine, and overloading takes care of it. In Perl, it is difficult specifically for strings, because strings might really be numbers. But avoid the issue and look at comparing Dogs. Clearly the max function can know what to do with Dogs, if Dogs are set up in a standard manner to be ordered.

In Perl, > is for numbers and gt is for strings. In Perl 6, after is for "canonical ordering" native to the type. So, if we were writing the max function, using after for the comparison will work natively on whichever type was passed.

In some languages, you would define a template function in such a way that it took two arguments of the same class. Or in this case we want one or more arguments, of the same type. In Perl 6 you can do that:

sub max (::Type *$first, Type *@rest --> Type) { ... }
But, we still run into the issue that we don't always want to use canonical comparisons. In the original example, we were not interested in string comparisons, but in comparing the lengths of the strings. So, next take a page from the C++ STL book. Many algorithms take a comparison operator that defaults to the standard ordering. But you can pass in something else!

Write the max function to take such an extra argument, and still take any number of value arguments, by making this one named.

sub max ( ::Type *$first, Type *@rest # one or more values in list context :&compare:(Type $, Type $ --> Bool) = &[less] # function, with de +fault --> Type) # returns the same type as the arguments { ... }
Now you can pass in the function to use. And a small function is fine. It doesn't need to close over any local variables.
my $comp = { length($^left) > length($^right) } my $result = max @values :compare<$comp>; # use adverb syntax
And that makes me hope that the built-in max function has such a feature too. In any case, I used a single function to customize the logic. Why a function instead of a whole object? Well, the object itself also customizes the normal comparison operators. This immediate custom thing only needed a single simple function, so why go overboard?

Now you can also bind parameters to functions to get another function, in a simple way. So you might not have to wrap it in another trivial function. So if I was passing my max function as another argument, but really wanted the customized comparsion, just use &max.assuming(:compare $comp) for that function. The point here is that state information doesn't require "objects", but might be done using closures, or even a tamed specialized form of closure, which is the ability to create new functions on the fly at run time.

In a wood working or repair project, I may pick out a very sharp chisel, a hand scraper, some sandpaper, a rasp, and play with each one a bit on the work at hand to decide on the final approach. I may end up going to get the dremmel tool instead. Each tool has overlapping capabilities, and often the one at hand is "better" than going to get a different one that might have been the better choice. I spoke with someone yesterday who was scared of TIMTOWDI. He didn't call it that, but explained that having more than one way to do simple things scared him away from Perl. But it's not a Frankenlanguage (well...), it's just like these wood tools: nobody thinks it odd that there are different ways to do it. There are just different tools and techniques.

For a library design, I would hopefully study the issue and come up with the optimum blend of overloading, derived classes, parameters, and callbacks. For a one-off, I'll just use whatever I grab first.


Replies are listed 'Best First'.
Re: TIMTOWTDI, synergy
by John M. Dlugosz (Monsignor) on Jun 02, 2009 at 19:02 UTC
    Let me continue by pointing out that in Perl 6 you would not write max as a list operator or function. Rather, write it as an infix (2-argument) function, and the system will automatically generate the matching reduction operator. Or, write the reduction operator yourself if that's more efficient, but use the reduction syntax:
    sub infix:<max> (::Type $left, Type $right --> Type) { return $left after $right ?? $left !! $right; } sub prefix:<[max]> (::Type *$first, Type *@rest --> Type) { ... }
    That leaves off the details of how to make it a chaining operator (I guess max chains through normal association) and declaring its level of precedence and associativity, etc.

Log In?

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://767727]
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (2)
As of 2022-07-04 06:56 GMT
Find Nodes?
    Voting Booth?

    No recent polls found