Has the ban on returning function nested structs been lifted?

spir denis.spir at gmail.com
Sat Mar 19 05:05:59 PDT 2011


On 03/19/2011 10:27 AM, Simen kjaeraas wrote:
> On Fri, 18 Mar 2011 23:48:53 +0100, bearophile <bearophileHUGS at lycos.com> wrote:
>
>> Jonathan M Davis:
>>
>>> Actually, the coolest part about it IMHO is that it highlights the fact that
>>> you
>>> should be using auto with std.algorithm and _not_ care about the exact types of
>>> the return types. Knowing the exact return type for those functions is
>>> generally
>>> unnecessary and is often scary anyway (especially with the functions which
>>> return lazy ranges like map and until). Making the functions return auto and
>>> completely hiding the return type pretty much forces the issue. There's still
>>> likely to be some confusion for those new to D, but it makes the proper way to
>>> use std.algorithm more obvious. I'd hate to deal with any code which used
>>> std.algorithm without auto. That would get ugly _fast_.
>>
>> auto variable inference is indeed almost necessary if you want to use lazy
>> functions as the ones in Phobos. But I have to say that those types are scary
>> because of the current design of those Phobos higher order functions. In
>> Haskell if you have an iterable and you perform a map on it using a function
>> that returns an int, you produce something like a [Int], that's a lazy list
>> of machine integers. This is a very simple type. If you perform another map
>> on that list, and the mapping function returns an int again, the type of the
>> whole result is [Int] still. The type you work with doesn't grow more and
>> more as with Phobos functions. Designers of C# LINQ have found a more complex
>> solution, they build a tree of lazy delegates...
>
> And we can have something similar in D:
>
> struct Range( T ) {
> void delegate( ) popFrontDg;
> bool delegate( ) emptyDg;
> T delegate( ) frontDg;
>
> this( R )( R range ) if ( isForwardRange!R && is( ElementType!R == T ) ) {
> auto rng = range.save();
> popFrontDg = ( ){ rng.popFront(); };
> emptyDg = ( ){ return rng.empty; };
> frontDg = ( ){ return rng.front; };
> }
>
> @property T front( ) {
> return frontDg( );
> }
>
> @property bool empty( ) {
> return emptyDg( );
> }
>
> void popFront( ) {
> popFrontDg( );
> }
> }
>
> Range!(ElementType!R) range( R )( R rng ) if ( isForwardRange!R ) {
> return Range!(ElementType!R)( rng );
> }
>
>
> There are times when I've wanted something like this because I don't
> know the resultant type of a bunch of range operations, but have to
> save it in a struct or class.

I guess something similar should be the base design of ranges. "Range of X" 
could simply mean "lazy sequence of X", an on-demand array (lol); and that 
would be the return type of every function returning a range. The complexity 
(of filter-ing, map-ping, find-ind) could be hidden inside the object, not 
exposed in the outer type.

Denis
-- 
_________________
vita es estrany
spir.wikidot.com



More information about the Digitalmars-d mailing list