Thread overview
Creating InputRanges from strings, files etc.
Nov 08, 2018
Vinay Sajip
Nov 08, 2018
rikki cattermole
Nov 08, 2018
Paul Backus
Nov 08, 2018
Vinay Sajip
Nov 08, 2018
Vinay Sajip
Nov 08, 2018
Alex
Nov 08, 2018
Vinay Sajip
November 08, 2018
Excuse my ignorance, but from looking at the documentation on std.range and a quick skim of the guides mentioned there near the top, I can't see what the simple way is of creating an InputRange!(ubyte) from strings, files etc. I would have expected to find something in the DLang Tour about this, but couldn't find anything. Please can someone tell me how to do it? Just to be clear, I want to create an object which is an InputRange!(ubyte) and pass that around, rather than e.g. iterate over a string or a file.
November 09, 2018
On 09/11/2018 2:58 AM, Vinay Sajip wrote:
> Excuse my ignorance, but from looking at the documentation on std.range and a quick skim of the guides mentioned there near the top, I can't see what the simple way is of creating an InputRange!(ubyte) from strings, files etc. I would have expected to find something in the DLang Tour about this, but couldn't find anything. Please can someone tell me how to do it? Just to be clear, I want to create an object which is an InputRange!(ubyte) and pass that around, rather than e.g. iterate over a string or a file.

TLDR of how to write an input range:

struct MyInputRange {
	ubyte[] input;

	@property {
		ubyte front() {
			return this.input[0];
		}

		bool empty() {
			return this.input.length == 0;
		}
	}

	void popFront() {
		this.input = this.input[1 .. $];
	}
}

import std.stdio;

void main() {
    foreach(b; MyInputRange([1, 2, 3])) {
    	writeln(b);
    }
}
November 08, 2018
On Thursday, 8 November 2018 at 13:58:55 UTC, Vinay Sajip wrote:
> Excuse my ignorance, but from looking at the documentation on std.range and a quick skim of the guides mentioned there near the top, I can't see what the simple way is of creating an InputRange!(ubyte) from strings, files etc. I would have expected to find something in the DLang Tour about this, but couldn't find anything. Please can someone tell me how to do it? Just to be clear, I want to create an object which is an InputRange!(ubyte) and pass that around, rather than e.g. iterate over a string or a file.

You can iterate through a file one ubyte at a time using `byChunk` and `joiner`:

    auto r1 = stdin.byChunk(1024).joiner;
    assert(is(typeof(r1.front) == ubyte));

You can iterate through a string one ubyte at a time using `representation`:

    auto r2 = "To be or not to be".representation;
    assert(is(typeof(r2.front) == immutable(ubyte)));

To pass these ranges around using the `InputRange` interface, use `inputRangeObject` to wrap them:

    InputRange!ubyte r3 = inputRangeObject(r1);
    InputRange!(immutable(ubyte)) r4 = inputRangeObject(r2);
November 08, 2018
On Thursday, 8 November 2018 at 14:38:37 UTC, Paul Backus wrote:
> You can iterate through a file one ubyte at a time using `byChunk` and `joiner`:
>
>     auto r1 = stdin.byChunk(1024).joiner;
>     assert(is(typeof(r1.front) == ubyte));
>
> You can iterate through a string one ubyte at a time using `representation`:
>
>     auto r2 = "To be or not to be".representation;
>     assert(is(typeof(r2.front) == immutable(ubyte)));
>
> To pass these ranges around using the `InputRange` interface, use `inputRangeObject` to wrap them:
>
>     InputRange!ubyte r3 = inputRangeObject(r1);
>     InputRange!(immutable(ubyte)) r4 = inputRangeObject(r2);

Aha - inputRangeObject was the thing I was missing. Thanks!
November 08, 2018
On Thursday, 8 November 2018 at 14:38:37 UTC, Paul Backus wrote:
> To pass these ranges around using the `InputRange` interface, use `inputRangeObject` to wrap them:
>
>     InputRange!ubyte r3 = inputRangeObject(r1);
>     InputRange!(immutable(ubyte)) r4 = inputRangeObject(r2);

I did a bit more digging, and it seems to work for strings but not for files: The program

import std.algorithm.iteration;
import std.format;
import std.range;
import std.stdio;
import std.string;

void somefn(InputRange!(immutable(ubyte)) r) {
    writeln(format!"%s"(r));
}

void main()
{
    auto a = "Hello, world!";
    auto b = inputRangeObject(a.representation);
    somefn(b);
    auto c = stdin.byChunk(1024).joiner;
    auto d = inputRangeObject(c);
    //somefn(d);
}

compiles as given above, but if the somefn(d) line is uncommented, I get an error:

function onlineapp.somefn(InputRange!(immutable(ubyte)) r) is not callable using argument types (InputRangeObject!(Result))
onlineapp.d(18):        cannot pass argument d of type std.range.interfaces.InputRangeObject!(Result) to parameter InputRange!(immutable(ubyte)) r

Do I need to do an explicit cast? If so, can someone tell me the precise incantation? How come it doesn't figure out that the underlying range is a ubyte range, or is it to do with immutability, or something else altogether?
November 08, 2018
On Thursday, 8 November 2018 at 16:15:25 UTC, Vinay Sajip wrote:
> On Thursday, 8 November 2018 at 14:38:37 UTC, Paul Backus wrote:
>> To pass these ranges around using the `InputRange` interface, use `inputRangeObject` to wrap them:
>>
>>     InputRange!ubyte r3 = inputRangeObject(r1);
>>     InputRange!(immutable(ubyte)) r4 = inputRangeObject(r2);
>
> I did a bit more digging, and it seems to work for strings but not for files: The program
>
> import std.algorithm.iteration;
> import std.format;
> import std.range;
> import std.stdio;
> import std.string;
>
> void somefn(InputRange!(immutable(ubyte)) r) {
>     writeln(format!"%s"(r));
> }
>
> void main()
> {
>     auto a = "Hello, world!";
>     auto b = inputRangeObject(a.representation);
>     somefn(b);
>     auto c = stdin.byChunk(1024).joiner;
>     auto d = inputRangeObject(c);
>     //somefn(d);
> }
>
> compiles as given above, but if the somefn(d) line is uncommented, I get an error:
>
> function onlineapp.somefn(InputRange!(immutable(ubyte)) r) is not callable using argument types (InputRangeObject!(Result))
> onlineapp.d(18):        cannot pass argument d of type std.range.interfaces.InputRangeObject!(Result) to parameter InputRange!(immutable(ubyte)) r
>
> Do I need to do an explicit cast? If so, can someone tell me the precise incantation? How come it doesn't figure out that the underlying range is a ubyte range, or is it to do with immutability, or something else altogether?

you could use a template for somefn definition:

´´´
void somefn(T)(T r) {
    writeln(format!"%s"(r));
}
´´´
November 08, 2018
On 11/8/18 11:15 AM, Vinay Sajip wrote:
> On Thursday, 8 November 2018 at 14:38:37 UTC, Paul Backus wrote:
>> To pass these ranges around using the `InputRange` interface, use `inputRangeObject` to wrap them:
>>
>>     InputRange!ubyte r3 = inputRangeObject(r1);
>>     InputRange!(immutable(ubyte)) r4 = inputRangeObject(r2);
> 
> I did a bit more digging, and it seems to work for strings but not for files: The program
> 
> import std.algorithm.iteration;
> import std.format;
> import std.range;
> import std.stdio;
> import std.string;
> 
> void somefn(InputRange!(immutable(ubyte)) r) {
>      writeln(format!"%s"(r));
> }
> 
> void main()
> {
>      auto a = "Hello, world!";
>      auto b = inputRangeObject(a.representation);
>      somefn(b);
>      auto c = stdin.byChunk(1024).joiner;
>      auto d = inputRangeObject(c);
>      //somefn(d);
> }
> 
> compiles as given above, but if the somefn(d) line is uncommented, I get an error:
> 
> function onlineapp.somefn(InputRange!(immutable(ubyte)) r) is not callable using argument types (InputRangeObject!(Result))
> onlineapp.d(18):        cannot pass argument d of type std.range.interfaces.InputRangeObject!(Result) to parameter InputRange!(immutable(ubyte)) r
> 
> Do I need to do an explicit cast? If so, can someone tell me the precise incantation? How come it doesn't figure out that the underlying range is a ubyte range, or is it to do with immutability, or something else altogether?

A cool feature of D is to have it tell you something about your code at compile time.

I did this in a run.dlang.org playground:

pragma(msg, ElementType!(typeof(b)));
pragma(msg, ElementType!(typeof(d)));

I get:
immutable(ubyte)
ubyte

Which means they aren't the same type, and they don't define the same interface (InputRange!(ubyte) is not the same as InputRange!(immutable(ubyte)) ).

Other than simply using compile-time functions, and dropping the object interface as Alex suggests, the easiest thing I can recommend is wrapping representation into a casting input range such as map:

auto b = inputRangeObject(a.representation.map!(b => ubyte(b)));

You can see all this here:

https://run.dlang.io/is/1E6Uqj

-Steve
November 08, 2018
On Thursday, 8 November 2018 at 16:41:50 UTC, Steven Schveighoffer wrote:
> I did this in a run.dlang.org playground:
>
> pragma(msg, ElementType!(typeof(b)));
> pragma(msg, ElementType!(typeof(d)));
>
> I get:
> immutable(ubyte)
> ubyte
>
> Which means they aren't the same type, and they don't define the same interface (InputRange!(ubyte) is not the same as InputRange!(immutable(ubyte)) ).
>
> Other than simply using compile-time functions, and dropping the object interface as Alex suggests, the easiest thing I can recommend is wrapping representation into a casting input range such as map:
>
> auto b = inputRangeObject(a.representation.map!(b => ubyte(b)));
>
> You can see all this here:
>
> https://run.dlang.io/is/1E6Uqj
>
> -Steve

Thanks, guys, those are helpful pointers.