August 12, 2014
On Tue, Aug 12, 2014 at 08:23:30PM +0000, Jonathan M Davis via Digitalmars-d-learn wrote:
> On Tuesday, 12 August 2014 at 19:03:58 UTC, H. S. Teoh via Digitalmars-d-learn wrote:
> >tl;dr: there are so many ways template code can go wrong, that I don't it justifies blaming alias this for problems.
> 
> Allowing implicit conversions makes the problem much worse IMHO. It makes it far too easy to write a template constraint which passes due to the implicit conversion (even if an implicit conversion wasn't explicitly checked for) but then have the function fail to work properly because the implicit conversion never actually takes place within the function (and if the template constraint doesn't explicitly test for an implicit conversion, then the argument that the value should have been explicitly converted doesn't hold). Fortunately, in many cases, the result is a compilation error rather than weird behavior, but in some cases, it will be weird behavior - especially when the code involved is highly templatized and generic.
[...]

Y'know, after seeing the recent problem with deprecated functions in template code, and now this, I'm starting to reconsider whether Concepts might have been a better way to go. The good thing about concepts is that the template body won't even compile if it makes unfounded assumptions on the type that aren't given by what the concept defines.

In the current template system, code like this would easily compile:

	auto badCode(T)(T t)
	{
		// Does T even support such an operation? Who knows. The
		// compiler will happily accept this code, and if you
		// only unittest this function with numeric types,
		// you'll never know there was a problem.
		return t+1;
	}

In a concepts-based system, however, this code wouldn't compile, even before you instantiate any templates. You'd have to define a concept that supports the opBinary!"+"(int) operation before the compiler would accept the code. And doing so enforces all incoming types to conform to that concept or be instantly rejected. This causes the templated code to be unable to do anything with the incoming type that isn't explicitly specified in the concept, thus preventing unfounded assumptions. It furthermore solves the alias this problem, because it would be clear to the compiler which concept the function is supposed to be operating on, and if the incoming type implicitly converts to something that implements that concept, then the compiler knows to perform the conversion first before perform an operation on it.

Now, it's true that concepts-based systems have their limitations.  But I wonder if it's possible to extend a concepts-based system to be more powerful, in the same way D has enhanced and developed C++ templates in novel ways.

One way I can think of, that kinda sits between the current duck-typing template system and a full-fledged concepts system, is one where incoming types do not have to declare what concept they implement (thus, a kind of duck-typing), but template functions are not allowed to operate on an incoming type unless they declare a concept it must conform to. Hypothetical syntax:

	// Concept definition
	concept InputRange(E) {
		alias ElementType = E;
		bool empty;
		E front;
		void popFront();
	}

	// Concrete type that implements InputRange(E)
	// N.B.: don't need to declare what concept it implements: this
	// makes it compatible with current code.
	struct MyRange {
		@property bool empty() { ... }
		@property int front() { ... }
		void popFront() { ... }

		// N.B.: this is *not* part of the concept definition
		@property MyRange save() { ... }
	}

	auto r = rangeFunc(MyRange.init);

	auto rangeFunc(R : InputRange!E, E)(R range)
	{
		while (!range.empty) // OK, .empty defined in InputRange!E
		{
			auto e = range.front; // OK, .front defined in InputRange!E

			if (someCond) {
				// ERROR: even though MyRange does
				// define .save, InputRange doesn't, and
				// we have only declared R to be an
				// InputRange, so .save is an undefined
				// operation on R.
				auto tmp = range.save;
			}

			// OK: .popFront is defined in InputRange!E
			range.popFront();
		}
		return ...;
	}


T

-- 
There are two ways to write error-free programs; only the third one works.
August 12, 2014
On 8/12/14, 6:31 PM, H. S. Teoh via Digitalmars-d-learn wrote:
> On Tue, Aug 12, 2014 at 08:23:30PM +0000, Jonathan M Davis via Digitalmars-d-learn wrote:
>> On Tuesday, 12 August 2014 at 19:03:58 UTC, H. S. Teoh via
>> Digitalmars-d-learn wrote:
>>> tl;dr: there are so many ways template code can go wrong, that I
>>> don't it justifies blaming alias this for problems.
>>
>> Allowing implicit conversions makes the problem much worse IMHO. It
>> makes it far too easy to write a template constraint which passes due
>> to the implicit conversion (even if an implicit conversion wasn't
>> explicitly checked for) but then have the function fail to work
>> properly because the implicit conversion never actually takes place
>> within the function (and if the template constraint doesn't explicitly
>> test for an implicit conversion, then the argument that the value
>> should have been explicitly converted doesn't hold). Fortunately, in
>> many cases, the result is a compilation error rather than weird
>> behavior, but in some cases, it will be weird behavior - especially
>> when the code involved is highly templatized and generic.
> [...]
>
> Y'know, after seeing the recent problem with deprecated functions in
> template code...

Duck typing FTW
1 2
Next ›   Last »