July 21, 2014
On Sunday, 20 July 2014 at 15:45:37 UTC, Atila Neves wrote:
> On Thursday, 17 July 2014 at 22:52:37 UTC, Justin Whear wrote:
>> On Thu, 17 Jul 2014 22:49:30 +0000, Nordlöw wrote:
>>
>>> AFAIK there is no compile-time variant of interfaces right?
>>> 
>>> Why is that?
>>> 
>>> Wouldn't it be nice to say something like
>>> 
>>>     struct SomeRange realize InputRange {
>>>         /* implement members of InputRange */
>>>     }
>>> 
>>> and then the compiler will statically check that that all members are
>>> implemented correctly.
>>> 
>>> I guess this requires some new syntax to describe what an InputRange is.
>>> 
>>> Kind of like C++ Concepts.
>>
>> What benefits would accrue from adding this?  Static verification that a
>> structure implements the specified concepts?  If so, you can simply do
>> this instead:
>>
>> static assert(isInputRange!SomeRange);
>
> This is sufficient, but not adequate. Just as the built-in
> unittest blocks with assertions, it's great when the assertion is
> true but good luck finding out where the bug is when it's not.
>
> The D Cookbook has an idiom to handle this by checking for __ctfe
> but it's super hacky and there should be a better way.
>
> I have lost count of how many times I wish the compiler would
> help me with compile time interfaces as it does with runtime
> code. static override and static interface? Yes please.
>
> Atila

+1, failing template constraints just gives a vague "couldn't match overload" type of error, and sometimes static assertions get suppressed. I've noticed that opDispatch is particularly bad about this. Even syntactic errors won't trigger compiler messages, and instead seems to behave like SFINAE which I was assured doesn't exist in D.
I have to use pragma (msg, ...) to get meaningful errors. Its so bad I generally avoid opDispatch despite its awesome potential and just generate template functions with mixins instead, because they are marginally easier to debug.

I wind up doing things like this to get the functionality I want:

static string assert_processing_stage_defined (string stage)()
{
	static immutable error_msg = `"Model must define processing stage: ` ~stage~ ` ()"`;

	return q{
		static assert (hasMember!(This, } `"`~stage~`"` q{), } ~error_msg~ q{);
		static assert (isSomeFunction!(__traits(getMember, This, } `"`~stage~`"` q{)), } ~error_msg~ q{);
		static assert (ParameterTypeTuple!(__traits(getMember, This, } `"`~stage~`"` q{)).length == 0, } ~error_msg~ q{);
		static assert (is (ReturnType!(__traits(getMember, This, } `"`~stage~`"` q{)) == void), } ~error_msg~ q{);
	};
}

mixin(``
	~assert_processing_stage_defined!`initialize`
	~assert_processing_stage_defined!`update`
);

I'm sure theres worse ways to do it but I still find this ugly and overly specific. I would much rather use something like what Nordlöw suggested. Something that is standardized across the language and generates meaningful error messages.
July 23, 2014
Could we possibly have a template like the following:

satisfiesInterface!(T, Interface);

that would return true when T satisfies Interface and otherwise gives descriptive error messages via __pragma(error, ...) ?

Then isInputRange could simply use satisfiesInterface under the hood and user code had a nice tool to check their very own requirements.
I'm not sure if such a template is possible though.
July 23, 2014
On Wed, Jul 23, 2014 at 04:21:16PM +0200, bossfong via Digitalmars-d-learn wrote:
> Could we possibly have a template like the following:
> 
> satisfiesInterface!(T, Interface);
> 
> that would return true when T satisfies Interface and otherwise gives descriptive error messages via __pragma(error, ...) ?
> 
> Then isInputRange could simply use satisfiesInterface under the hood and user code had a nice tool to check their very own requirements. I'm not sure if such a template is possible though.

We could, though it's not quite the same as a native concepts implementation where the compiler can check templates for code that wrongly makes assumptions about the incoming type that aren't defined by the concept.


T

-- 
I think Debian's doing something wrong, `apt-get install pesticide', doesn't seem to remove the bugs on my system! -- Mike Dresser
July 23, 2014
Am 23.07.2014 16:27, schrieb H. S. Teoh via Digitalmars-d-learn:
>
> We could, though it's not quite the same as a native concepts
> implementation where the compiler can check templates for code that
> wrongly makes assumptions about the incoming type that aren't defined by
> the concept.
>
>
> T
>
true.

Still, maybe compiler errors could be provided by a library that defines an "Concept(Interface)" UDA that you could use to annotate implementations of "Concepts"?

import concepts;

@Concept(InputRange)
struct MyInputRange
{
// ...
}

verifyConcepts();

Is it that what you mean?

Just thinking things through here...
July 23, 2014
On Wed, Jul 23, 2014 at 04:46:20PM +0200, bossfong via Digitalmars-d-learn wrote:
> Am 23.07.2014 16:27, schrieb H. S. Teoh via Digitalmars-d-learn:
> >
> >We could, though it's not quite the same as a native concepts implementation where the compiler can check templates for code that wrongly makes assumptions about the incoming type that aren't defined by the concept.
> >
> >
> >T
> >
> true.
> 
> Still, maybe compiler errors could be provided by a library that defines an "Concept(Interface)" UDA that you could use to annotate implementations of "Concepts"?
> 
> import concepts;
> 
> @Concept(InputRange)
> struct MyInputRange
> {
> // ...
> }
> 
> verifyConcepts();
> 
> Is it that what you mean?
> 
> Just thinking things through here...

No, I'm talking about catching errors like this:

	auto myRangeAlgo(R)(R range)
		if (isInputRange!R)
	{
		...
		auto r = range.save;	// <--- oops
		...
		return Result(...);
	}

	unittest
	{
		auto testData = [1,2,3];
		auto result = myRangeAlgo(testData);

		// Test will pass, because arrays are also forward
		// ranges, which have a .save method. So we fail to
		// catch the bug in the code above.
		assert(result.equal(expectedResults));
	}


T

-- 
Bomb technician: If I'm running, try to keep up.
July 23, 2014
On Wednesday, 23 July 2014 at 15:28:34 UTC, H. S. Teoh via Digitalmars-d-learn wrote:
> On Wed, Jul 23, 2014 at 04:46:20PM +0200, bossfong via Digitalmars-d-learn wrote:
>> Am 23.07.2014 16:27, schrieb H. S. Teoh via Digitalmars-d-learn:
>> >
>> >We could, though it's not quite the same as a native concepts
>> >implementation where the compiler can check templates for code that
>> >wrongly makes assumptions about the incoming type that aren't defined by
>> >the concept.
>> >
>> >
>> >T
>> >
>> true.
>> 
>> Still, maybe compiler errors could be provided by a library that
>> defines an "Concept(Interface)" UDA that you could use to annotate
>> implementations of "Concepts"?
>> 
>> import concepts;
>> 
>> @Concept(InputRange)
>> struct MyInputRange
>> {
>> // ...
>> }
>> 
>> verifyConcepts();
>> 
>> Is it that what you mean?
>> 
>> Just thinking things through here...
>
> No, I'm talking about catching errors like this:
>
> 	auto myRangeAlgo(R)(R range)
> 		if (isInputRange!R)
> 	{
> 		...
> 		auto r = range.save;	// <--- oops
> 		...
> 		return Result(...);
> 	}
>
> 	unittest
> 	{
> 		auto testData = [1,2,3];
> 		auto result = myRangeAlgo(testData);
>
> 		// Test will pass, because arrays are also forward
> 		// ranges, which have a .save method. So we fail to
> 		// catch the bug in the code above.
> 		assert(result.equal(expectedResults));
> 	}
>
>
> T

What about a small wrapper struct then:
A struct Concept that implements opdispatch and forwards all calls specified in the interface but for every other method do a pragma error.
Then take this struct as a parameter in the function instead of any type T.
Maybe, if that is possible, allow implicit conversion from any type to Concept, so the callsite doesnt change.
Is there something obvious im missing? Im justbrainstorming here...
July 23, 2014
On Wed, Jul 23, 2014 at 05:09:54PM +0000, Bossfong via Digitalmars-d-learn wrote:
> On Wednesday, 23 July 2014 at 15:28:34 UTC, H. S. Teoh via Digitalmars-d-learn wrote:
> >On Wed, Jul 23, 2014 at 04:46:20PM +0200, bossfong via Digitalmars-d-learn wrote:
[...]
> >>Still, maybe compiler errors could be provided by a library that defines an "Concept(Interface)" UDA that you could use to annotate implementations of "Concepts"?
> >>
> >>import concepts;
> >>
> >>@Concept(InputRange)
> >>struct MyInputRange
> >>{
> >>// ...
> >>}
> >>
> >>verifyConcepts();
> >>
> >>Is it that what you mean?
> >>
> >>Just thinking things through here...
> >
> >No, I'm talking about catching errors like this:
> >
> >	auto myRangeAlgo(R)(R range)
> >		if (isInputRange!R)
> >	{
> >		...
> >		auto r = range.save;	// <--- oops
> >		...
> >		return Result(...);
> >	}
> >
> >	unittest
> >	{
> >		auto testData = [1,2,3];
> >		auto result = myRangeAlgo(testData);
> >
> >		// Test will pass, because arrays are also forward
> >		// ranges, which have a .save method. So we fail to
> >		// catch the bug in the code above.
> >		assert(result.equal(expectedResults));
> >	}
> >
> >
> >T
> 
> What about a small wrapper struct then:
> A struct Concept that implements opdispatch and forwards all calls
> specified in the interface but for every other method do a pragma
> error.
> Then take this struct as a parameter in the function instead of any
> type T.  Maybe, if that is possible, allow implicit conversion from
> any type to Concept, so the callsite doesnt change.
> Is there something obvious im missing? Im justbrainstorming here...

That's not a bad idea, and in fact something similar occurred to me as well. Basically, my idea was to create dummy structs representing the Concepts checked by the sig constraints, and have unittests instantiate the algorithm with said structs. The structs will only have the methods defined by the Concept, so this will catch any wrong code that tries to access something outside of what the Concept defines.

Your idea is better, in fact, since you wouldn't have to rely on programmer diligence to write such unittests. You wouldn't even need to use pragma error; you just have opDispatch forward only the functions implemented by the concept, and everything else will result in a "no such field" error.

The only trouble is implicit conversion. I don't think the language allows that right now, by design.


T

-- 
Question authority. Don't ask why, just do it.
1 2
Next ›   Last »