View mode: basic / threaded / horizontal-split · Log in · Help
November 09, 2012
Re: UDAs - Restrict to User Defined Types?
On 11/8/2012 8:18 PM, Adam D. Ruppe wrote:
> On Friday, 9 November 2012 at 03:45:11 UTC, Nick Sabalausky wrote:
>> the *one* thing I hate about D ranges is that they don't force you to
>> explicitly say "Yes, I *intend* this to be an InputRange" (what are we, Go
>> users?).
>
> Just a note, of course it still wouldn't *force*, but maybe it'd be a good habit
> to start writing this:
>
> struct myrange {...}
> static assert(isInputRange!myrange);
>
> It'd be a simple way to get a check at the point of declaration and to document
> your intent.
>
>
> Interestingly, we could also do this if the attributes could run through a
> template:
>
> [check!isInputRange] struct myrange{}
>
> @attribute template check(something, Decl) {
>     static assert(something!Decl);
>     alias check = Decl;
> }


Many algorithms (at least the ones in Phobos do) already do a check to ensure 
the inputs are the correct kind of range. I don't think you'll get very far 
trying to use a range that isn't a range.

Of course, you can always still have bugs in your range implementation.
November 09, 2012
Re: UDAs - Restrict to User Defined Types?
On Thursday, November 08, 2012 21:10:55 Walter Bright wrote:
> Many algorithms (at least the ones in Phobos do) already do a check to
> ensure the inputs are the correct kind of range. I don't think you'll get
> very far trying to use a range that isn't a range.
> 
> Of course, you can always still have bugs in your range implementation.

Given that a range requires a very specific set of functions, I find it highly 
unlikely that anything which isn't a range will qualify as one. It's far more 
likely that you screw up and a range isn't the right kind of range because one 
of the functions wasn't quite right.

There is some danger in a type being incorrectly used with a function when 
that function requires and tests for only one function, or maybe when it 
requires two functions. But I would expect that as more is required by a 
template constraint, it very quickly becomes the case that there's no way that 
any type would ever pass it with similarly named functions that didn't do the 
same thing as what they were expected to do. It's just too unlikely that the 
exact same set of function names would be used for different things, especially 
as that list grows. And given that ranges are a core part of D's standard 
library, I don't think that there's much excuse for having a type that has the 
range functions but isn't supposed to be a range. So, I really don't see this 
as a problem.

- Jonathan M Davis
November 09, 2012
Re: UDAs - Restrict to User Defined Types?
On Thu, Nov 08, 2012 at 11:51:29PM -0500, Nick Sabalausky wrote:
> On Thu, 8 Nov 2012 20:17:24 -0800
> "H. S. Teoh" <hsteoh@quickfur.ath.cx> wrote:
> > 
> > Actually, I just thought of a solution to the whole duck-typing range
> > thing:
> > 
> > 	struct MyRange {
> > 		// Self-documenting: this struct is intended to be a
> > 		// range.
> > 		static assert(isInputRange!MyRange,
> > 			"Dude, your struct isn't a range!"); //
> > asserts
> > 
> > 
> On Fri, 09 Nov 2012 05:18:59 +0100
> "Adam D. Ruppe" <destructionator@gmail.com> wrote:
> > 
> > Just a note, of course it still wouldn't *force*, but maybe it'd 
> > be a good habit to start writing this:
> > 
> > struct myrange {...}
> > static assert(isInputRange!myrange);
> > 
> 
> Those are only half-solutions as they only prevent false-negatives,
> not false-positives. Plus, there's nothing to prevent people from
> forgetting to do it in the first place.

IOW, you want the user-defined type to declare that it's an input range,
and not just some random struct that happens to have input range like
functions?

What about modifying isInputRange to be something like this:

	template isInputRange(R) {
		static if (R.implementsInputRange &&
			/* ... check for input range properties here */)
		{
			enum isInputRange = true;
		} else {
			enum isInputRange = false;
		}
	}

Then all input ranges will have to explicitly declare they are an input
range thus:

	struct MyInpRange {
		// This asserts that we're trying to be an input range
		enum implementsInputRange = true;

		// ... define .empty, .front, .popFront here
	}

Any prospective input range that doesn't define implementsInputRange
will be rejected by all input range functions. (Of course, that's just a
temporary name, you can probably think of a better one.)

You can also make it a mixin, or something like that, if you want to
avoid the tedium of defining an enum to be true every single time.


T

-- 
Long, long ago, the ancient Chinese invented a device that lets them see
through walls. It was called the "window".
November 09, 2012
Re: UDAs - Restrict to User Defined Types?
On 11/8/2012 9:24 PM, Jonathan M Davis wrote:
> So, I really don't see this as a problem.

Neither do I.

BTW, there's no compiler magic in the world that will statically guarantee you 
have a non-buggy implementation of a range.
November 09, 2012
Re: UDAs - Restrict to User Defined Types?
On Thursday, November 08, 2012 21:49:52 Walter Bright wrote:
> BTW, there's no compiler magic in the world that will statically guarantee
> you have a non-buggy implementation of a range.

That's what unit tests are for. :)

- Jonathan M Davis
November 09, 2012
Re: UDAs - Restrict to User Defined Types?
On 11/9/12, H. S. Teoh <hsteoh@quickfur.ath.cx> wrote:
> Then all input ranges will have to explicitly declare they are an input
> range thus:

Or:

Change definition of isInputRange to:

---
template isInputRange(T)
{
   enum bool isInputRange = (__attributes(T, RequiresInputRangeCheck)
                             && __attributes(T, IsInputRangeAttr))
       || is(typeof(
       (inout int _dummy=0)
       {
           R r = void;       // can define a range object
           if (r.empty) {}   // can test for empty
           r.popFront();     // can invoke popFront()
           auto h = r.front; // can get the front of the range
       }));
}
---

and in your user module:

---
module foo;
@RequiresInputRangeCheck:  // apply attribute to all declarations in this module

@IsInputRangeAttr struct MyRange { /* front/popFront/empty */ }
struct NotARange { /* front/popFront/empty defined but not designed to
be a range */ }
---

---
static assert(isInputRange!MyRange);
static assert(!(isInputRange!NotARange));
---

That way you keep compatibility with existing ranges and introduce
extra safety check for new types which want to be checked.
November 09, 2012
Re: UDAs - Restrict to User Defined Types?
On Thu, Nov 08, 2012 at 09:37:00PM -0800, H. S. Teoh wrote:
> On Thu, Nov 08, 2012 at 11:51:29PM -0500, Nick Sabalausky wrote:
[...]
> > Those are only half-solutions as they only prevent false-negatives,
> > not false-positives. Plus, there's nothing to prevent people from
> > forgetting to do it in the first place.
> 
> IOW, you want the user-defined type to declare that it's an input
> range, and not just some random struct that happens to have input
> range like functions?
> 
> What about modifying isInputRange to be something like this:
> 
> 	template isInputRange(R) {
> 		static if (R.implementsInputRange &&
> 			/* ... check for input range properties here */)
> 		{
> 			enum isInputRange = true;
> 		} else {
> 			enum isInputRange = false;
> 		}
> 	}
> 
> Then all input ranges will have to explicitly declare they are an
> input range thus:
> 
> 	struct MyInpRange {
> 		// This asserts that we're trying to be an input range
> 		enum implementsInputRange = true;
> 
> 		// ... define .empty, .front, .popFront here
> 	}
> 
> Any prospective input range that doesn't define implementsInputRange
> will be rejected by all input range functions. (Of course, that's just
> a temporary name, you can probably think of a better one.)
> 
> You can also make it a mixin, or something like that, if you want to
> avoid the tedium of defining an enum to be true every single time.
[...]

Here's a slight refinement:

	// Note: untested code
	mixin template imAnInputRange() {
		static assert(isInputRange!(typeof(this)));
		enum implementsInputRange = true;
	}

	struct MyInpRange {
		// This should croak loudly if this struct isn't a valid
		// input range. Omitting this line makes range functions
		// reject it too (provided we modify isInputRange as
		// described above).
		mixin imAnInputRange;

		// implement range functions here
	}


T

-- 
It only takes one twig to burn down a forest.
November 09, 2012
Re: UDAs - Restrict to User Defined Types?
On Thu, Nov 08, 2012 at 10:03:03PM -0800, Jonathan M Davis wrote:
> On Thursday, November 08, 2012 21:49:52 Walter Bright wrote:
> > BTW, there's no compiler magic in the world that will statically
> > guarantee you have a non-buggy implementation of a range.

Yeah, that's one major missing feature from D/Phobos/etc.: a mixin
template called EliminateBugs that will fix all your program's bugs for
you. I think that should be the next top priority on D's to-do list! ;-)


> That's what unit tests are for. :)
[...]

Well, unittests are a runtime check, and they don't *guarantee*
anything. (One could, in theory, write a pathological pseudo-range that
passes basic unittests but fail to behave like a range in some obscure
corner case. Transient ranges would fall under that category, should we
decide not to admit them as valid ranges. :-))

But of course that's just splitting hairs.


T

-- 
Amateurs built the Ark; professionals built the Titanic.
November 09, 2012
Re: UDAs - Restrict to User Defined Types?
On 11/8/2012 10:20 PM, H. S. Teoh wrote:
> But of course that's just splitting hairs.

"Let's not go splittin' hares!"
   -- Bugs Bunny
November 09, 2012
Re: UDAs - Restrict to User Defined Types?
On Thu, 8 Nov 2012 21:37:00 -0800
"H. S. Teoh" <hsteoh@quickfur.ath.cx> wrote:
> 
> IOW, you want the user-defined type to declare that it's an input
> range, and not just some random struct that happens to have input
> range like functions?
> 

Yes.

> What about modifying isInputRange to be something like this:
> 
> 	template isInputRange(R) {
> 		static if (R.implementsInputRange &&
> 			/* ... check for input range properties here
> */) {
> 			enum isInputRange = true;
> 		} else {
> 			enum isInputRange = false;
> 		}
> 	}
> 
> Then all input ranges will have to explicitly declare they are an
> input range thus:
> 
> 	struct MyInpRange {
> 		// This asserts that we're trying to be an input range
> 		enum implementsInputRange = true;
> 
> 		// ... define .empty, .front, .popFront here
> 	}
> 

Close. At that point you need two steps:

struct MyInpRange {
   // Declare this as an input range
   enum implementsInputRange = true;

   // Enforce this really *IS* as an input range
   static assert(isInputRange!MyRange,
       "Dude, your struct isn't a range!"); // asserts

   // ... define .empty, .front, .popFront here
}

My suggestion was to take basically that, and then wrap up the "Declare
and Enfore" in one simple step:

struct MyInpRange {

   // Generate & mixin *both* the "enum" and the "static assert"
   mixin(implements!InputRange);

   // ... define .empty, .front, .popFront here
}

/Dreaming:

Of course, it'd be even nicer still to have all this wrapped up in
some language sugar (D3? ;) ) and just do something like:

struct interface InputRange {
   // ... *declare* .empty, .front, .popFront here
}

struct interface ForwardRange : InputRange {
   // ... *declare* .save here
}

struct MyForwardRange : ForwardRange {
   // ... define .empty, .front, .popFront, .save here
   // Actually validated by the compiler
}

Which would then amount to what we're doing by hand up above. So kinda
like Go, except not error-prone and ducky and all shitty.
1 2 3 4 5
Top | Discussion index | About this forum | D home