Return type of std.algorithm.map

Jonathan M Davis jmdavisProg at gmx.com
Thu Jul 7 10:56:29 PDT 2011


On 2011-07-07 07:00, James Fisher 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), 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".
> 
> 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". 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".
> That's weird, because "std.range.InputRange!(int).InputRange" doesn't even
> look like a type.
> 
> I've tried all manner of combinations here, but nothing works. Seems like
> there's something fundamental I'm not understanding.

Really, the idea is that you never care about the return type for most of the 
functions in std.algorithm and std.range. It's a range, and you use it as a 
range. That means that it's guaranteed to have certain functions (empty, 
popFront, front, etc.), with the exact set of operations depending on the type 
of range that it is. If you need a particular type, then you convert it to 
what you want (e.g. you use something like std.array.array which converts a 
range to an array). You really shouldn't be caring about the exact return type 
of most of the functions in std.algorithm and std.range. And they're pretty 
much designed with the idea that you _won't_.

Now, if you really need the return type for whatever reason, then use typeof 
to get the type (be it typeof on the function call - e.g. typeof(map!((a)
{return a * a;})(start) - or type of the result which you gave the type of 
auto). But the idea is that the results of range functions are typically 
generic ranges where you don't know or care what their exact type is. And 
since they're ranges, you can even pass them to other range-based functions 
without caring about what their exact types are.

The one exception that I can think of is that when you need to remove elements 
from a container, you need the type of the range to either be the exact type 
of range that that container uses or a range that that container supports 
(which would generally be the result of take or takeExactly on the type of 
range that that container uses). Some functions will return the appropriate 
type (e.g. find), and others won't. But it should generally be obvious from 
what they do whether they will or not (since they need to return a slice of 
the original range rather than a new range composed of pieces of the 
original), and trying the result of a function from std.range or std.algorithm 
with a remove function for a container should tell you fairly quickly if 
there's any question.

So, really, the idea is that you don't generally know or care what the return 
type of a range-based function is and that if you do need it to be a 
particular type, then you convert it to that type.

- Jonathan M Davis


More information about the Digitalmars-d-learn mailing list