Fun with range deprecations

Fra via Digitalmars-d digitalmars-d at puremagic.com
Tue Aug 12 01:20:09 PDT 2014


On Monday, 11 August 2014 at 22:59:34 UTC, H. S. Teoh via 
Digitalmars-d wrote:
> So, a recent Phobos deprecation introduced a fun regression:
>
> 	https://issues.dlang.org/show_bug.cgi?id=13257
>
> While the bug was filed specifically for the use case
> range.splitter.map, the problem is actually much more general, 
> and far
> from obvious how to address.
>
> First, let's consider the following setup.  Start with this 
> example
> range:
>
> 	struct MyRange(T) {
> 		// input range
> 		@property bool empty() { ... }
> 		@property T front() { ... }
> 		void popFront() { ... }
>
> 		// forward range
> 		@property MyRange save() { ... }
>
> 		// bidirectional range
> 		@property T back() { ... }
> 		void popBack() { ... }
> 	}
>
> Suppose we write an algorithm that operates on ranges in 
> general,
> including MyRange. The current convention is to forward as many 
> range
> methods as possible, so that if you give it a bidi range, it 
> will, where
> possible, give you a wrapped bidirectional range. So we do this:
>
> 	auto myAlgo(R)(R range)
> 		if (isInputRange!R)
> 	{
> 		struct Result {
> 			// input range methods
> 			@property bool empty() { ... }
> 			@property U front() { ... }
> 			void popFront() { ... }
>
> 			// N. B. if we got a forward range, and we can
> 			// implement .save, then do so.
> 			static if (isForwardRange!R &&
> 				canDoForwardRange)
> 			{
> 				@property Result save() { ... }
> 			}
>
> 			// N. B. if we got a bidirectional range, and we
> 			// can implement .back and .popBack, then do so.
> 			static if (isBidirectionalRange!R &&
> 				canDoBidirectionalRange)
> 			{
> 				@property U back() { ... }
> 				void popBack() { ... }
> 			}
> 		}
> 		return Result(...);
> 	}
>
> Now suppose, for whatever reason, we decide that implement 
> MyRange as a
> bidirectional range was a bad idea, and we decide to deprecate 
> it:
>
> 	struct MyRange(T) {
> 		... // input & forward range API here
>
> 		deprecated("Use of MyRange as bidirectional range is now 
> deprecated")
> 		{
> 			@property T back() { ... }
> 			void popBack() { ... }
> 		}
> 	}
>
> Now the fun begins. Currently, isBidirectionalRange uses
> is(typeof(...)) to check if .back and .popBack exist in target 
> type.
> So when myAlgo() checks MyRange, this check passes, because 
> even though
> .back and .popBack have been deprecated, they obviously still 
> exist, so
> they do have a valid type. So that check passes. Therefore, 
> myAlgo()
> will try to export a bidirectional interface in its result.
>
> However, inside Result.back and Result.popBack, when it 
> actually tries
> to use MyRange.back and MyRange.popBack, the compiler throws up 
> its
> hands and say, Wait!! .back and .popBack are deprecated!!
>
> Note that this happens *regardless* of whether .back and 
> .popBack are
> actually used by the user code that calls myAlgo(). This means 
> that if
> user code only ever uses forward range capabilities of 
> myAlgo(), users
> will *still* get irrelevant deprecation messages -- for 
> bidirectional
> range features that they never use! Furthermore, the only reason
> myAlgo() added .back and .popBack which trigger the deprecation
> messages, is because the is(typeof(...)) check tests positive 
> for
> bidirectional range support.
>
> This problem, obviously, is not specific to bidirectional 
> ranges, or,
> for that matter, *any* range based code. It's a general problem 
> with
> wrapped types, of the form:
>
> 	struct Wrapped {
> 		deprecated void method();
> 	}
>
> 	struct Wrapper(T) {
> 		T t;
> 		static if (is(typeof(t.method())))
> 		{
> 			void func() {
> 				t.method();
> 			}
> 		}
> 	}
>
> The problem here is that t.method() is generic, and it has 
> specifically
> tested for the usability of t.method(), yet when it tries to 
> actually
> use t.method(), it hits a deprecation message. How is it 
> supposed to
> know if t.method() has been deprecated? Currently we have no
> __traits(deprecated) test. And even if we did, this may not be 
> the best
> solution (are we going to now insert __traits(deprecated) all 
> over
> generic code, on the off-chance that some random user type 
> somewhere
> will get deprecated in the unforeseeable future?).
>
> So the question now is: how do we deal with this issue? 
> Currently, this
> problem already exists in Phobos 2.067a, and random user code 
> that calls
> splitter(...).map(...) will hit this problem.
>
> So far the following have been suggested, none of which seem
> particularly satisfactory:
>
> 1) Make Wrapper.func() a template function, so that the 
> deprecation
> message is not triggered unless the user actually calls it (in 
> which
> case the deprecation message *should* be triggered). The 
> problem is that
> when the deprecation message *is* triggered, it comes from deep 
> inside
> Phobos, and users may complain, why did you export a 
> bidirectional range
> API if that support is already deprecated?
>
> 2) Add a __traits(deprecated) or is(T == deprecated) test, and 
> update
> all affected sig constraints in Phobos to check for this. 
> Doesn't sound
> like an appealing solution.
>
> 3) Have std.algorithm.map specifically check for a specific 
> overload of
> std.algorithm.splitter, and omit .back and .popBack in that one 
> case.
> Very hackish, solves the immediate problem, but leaves the 
> general
> problem unsolved. User code that wraps around splitter in a 
> similar way
> to map will *still* be broken (even if they never actually use
> bidirectional features).
>
> 4) Shorten the deprecation cycle of splitter. Doesn't even 
> solve the
> immediate problem, just shortens it, and still leaves the 
> general
> problem unsolved. User code that wraps around splitter is still 
> broken
> (even if they never actually use bidirectional features).
>
> Since no obvious acceptable solution seems forthcoming, I 
> thought we
> should bring this to the forum for discussion to see if anyone 
> has a
> better idea.
>
>
> T
Noob question... shouldn't __traits(compile) enable us to handle 
the situation correctly?


More information about the Digitalmars-d mailing list