Return type of std.algorithm.map
James Fisher
jameshfisher at gmail.com
Fri Jul 8 08:06:30 PDT 2011
OK, good replies. Cool. So the two places I thought I'd need to use
explicit types are in parameter and return types. Say, a function returns
the result of map, and another consumes it to print it. The consuming
function seems to work with templating:
void printSquares(T)(T squares) {
writeln(squares);
}
void main() {
int[] start = [1,2,3,4,5];
auto squares = map!((a) { return a * a; })(start);
printSquares(squares);
}
However, returning the result of map doesn't seem to work so well with the
same method:
T getSquares(T)() {
int[] start = [1,2,3,4,5];
return map!((a) { return a * a; })(start);
}
void main() {
auto squares = getSquares();
writeln(squares);
}
Gives the errors:
test.d(11): Error: template test.getSquares(T) does not match any function
template declaration
test.d(11): Error: template test.getSquares(T) cannot deduce template
function from argument types !()()
But this does work with auto return type inference:
auto getSquares() {
int[] start = [1,2,3,4,5];
return map!((a) { return a * a; })(start);
}
void main() {
auto squares = getSquares();
writeln(squares);
}
So ... stuff works, but I'm not really sure why one uses function templating
and the other uses return type inference. Any answers?
On Thu, Jul 7, 2011 at 7:31 PM, Steven Schveighoffer <schveiguy at yahoo.com>wrote:
> On Thu, 07 Jul 2011 10:00:48 -0400, James Fisher <jameshfisher at gmail.com>
> wrote:
>
> To date, I've been using D in much the same way I used C++, without heavy
>> use of templates. Now I'm trying out a more functional style using
>> std.algorithm. However I'm pretty much stuck at the first hurdle: map.
>> With type inference, this works:
>>
>> import std.algorithm;
>> import std.stdio;
>>
>> void main() {
>> auto start = [1,2,3,4,5];
>> auto squares = map!((a) { return a * a; })(start);
>> writeln(squares);
>> }
>>
>>
>> Without type inference (obviously necessary beyond trivial examples),
>>
>
> Type inference is useful everywhere, at any level of complexity.
>
> Where it's not useful is declarations, for example, declaring the type of a
> struct or class member. However, typeof should work to use type inference
> there.
>
>
> it'd
>> be nice to do:
>>
>> import std.algorithm;
>> import std.stdio;
>>
>> void main() {
>> int[] start = [1,2,3,4,5];
>> int[] squares = map!((a) { return a * a; })(start);
>> writeln(squares);
>> }
>>
>>
>> but this gives "Error: cannot implicitly convert expression (map(start))
>> of
>> type Result to int[]". That opaque type "Result" is weird (not
>> parameterized on "int"?), but OK, let's try that:
>>
>> import std.algorithm;
>> import std.stdio;
>>
>> void main() {
>> int[] start = [1,2,3,4,5];
>> Result squares = map!((a) { return a * a; })(start);
>> writeln(squares);
>> }
>>
>>
>> gives "undefined identifier Result". Not sure why, but OK. I can't see
>> examples in the docs of explicit type declarations -- annoyingly they all
>> use "auto".
>>
>
> OK, so this is kind of a weird case. std.algorithm.map returns an inner
> struct, which means, it has no public name. But then how can you even use
> it? Well, it just doesn't have a public *name*, but it still can be used
> outside the function. It's definitely an oddball. However, what's nice
> about it is that you can encapsulate the struct inside the one place it is
> used.
>
> You can see the definition of map here: https://github.com/D-**
> Programming-Language/phobos/**blob/master/std/algorithm.d#**L366<https://github.com/D-Programming-Language/phobos/blob/master/std/algorithm.d#L366>
>
> And a few lines down, the declaration of Result.
>
> You can use typeof if you want to get the type, but again, auto is much
> better unless you are declaring something.
>
>
> However, they do tell me that map "returns a range". Assuming all
>> definitions of ranges are in std.range, there's no such thing as a "Range"
>> interface, so it's not that. The most general interfaces I can see are
>> InputRange(E) and OutputRange(E). It certainly can't be an OutputRange,
>> so
>> I guess it must satisfy InputRange, presumably type-parameterized with
>> "int".
>>
>
> No, a range is a concept, meaning it is a compile-time interface. Any type
> which satisfies the requirements of the input range can be a range, even
> non-polymorphic types. Even an array is a range.
>
>
> So this should work:
>>
>> import std.algorithm;
>> import std.stdio;
>> import std.range;
>>
>> void main() {
>> int[] start = [1,2,3,4,5];
>> InputRange!int squares = map!((a) { return a * a; })(start);
>> writeln(squares);
>> }
>>
>>
>> But the compiler complains "cannot implicitly convert expression
>> (map(start)) of type Result to std.range.InputRange!(int).**InputRange".
>>
>
> Right, because it's not a derivative of that interface, it's its own type,
> defined only inside the function. Yeah, I know it's confusing :)
>
>
> That's weird, because "std.range.InputRange!(int).**InputRange" doesn't
>> even
>> look like a type.
>>
>
> std.range is the module, but I assume you already know that.
>
> But one of the coolest things about D templates is the eponymous rule.
> That is, if you declare a template, and that template has exactly one
> member, and that one member is named the same as the template, then x!(y)
> becomes the equivalent of x!(y).x.
>
> For example:
>
> template t(T)
> {
> class t
> {
> T val;
> }
> }
>
> t!(int) is equivalent to t!(int).t
>
> In other words, a template is a *namespace* for declarations using the
> template parameters. And in this special case, you can omit the member of
> the namespace you are accessing.
>
> Then what follows is that:
>
> class t(T)
> {
> T val;
> }
>
> is shorthand for the above.
>
> But the compiler maintains the namespace.member nomenclature, which is why
> you see that in the error message.
>
> -Steve
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.puremagic.com/pipermail/digitalmars-d-learn/attachments/20110708/6eaf451e/attachment-0001.html>
More information about the Digitalmars-d-learn
mailing list