On Tue, 14 Dec 2010 09:35:20 -0500
"Robert Jacques" <sandf...@jhu.edu> wrote:

> Having recently run into this without knowing it,

Which one? (This issue causes 3 distinct bugs.)

> vote++. Also, please  
> file a bug report (or two).

Done: http://d.puremagic.com/issues/show_bug.cgi?id=5354  -- see also below


Denis


====================== text of issue report ==========================
formatValue: range templates introduce 3 bugs related to class & struct cases

This issue concerns class case, the struct case, and the 3 range cases of the 
set of formatValue templates in std.format. As this set is currently written 
and commented (1), it seems to be intended to determine the following cases 
(about class/struct/range only):

* An input range is formatted like an array.
* A class object is formatted using toString.
* A struct is formatted:
    ~ using an input range interface, if it implements one,
    ~ using toString, if it defines it,
    ~ in last resort, using the type's 'stringof' property.

To be short: I think the right thing to do is to remove range cases. 
Explanations, details, & reasoning below.

In the way the set of templates is presently implemented, and because of how 
template selection works (as opposed to inheritance, eg), the following 3 bugs 
come up:

1. When a class defines an input range, compiler-error due to the fact that 
both class and input range cases match:
    /usr/include/d/dmd/phobos/std/format.d(1404): Error: template 
std.format.formatValue(Writer,T,Char) if (is(const(T) == const(void[]))) 
formatValue(Writer,T,Char) if (is(const(T) == const(void[]))) matches more than 
one template declaration, 
/usr/include/d/dmd/phobos/std/format.d(1187):formatValue(Writer,T,Char) if 
(isInputRange!(T) && !isSomeString!(T) && isSomeChar!(ElementType!(T))) and 
/usr/include/d/dmd/phobos/std/format.d(1260):formatValue(Writer,T,Char) if 
(is(T == class))
This, due to inheritance from Object, even if no toString is _explicitely_ 
defined.

2. For a struct, a programmer-defined output format in toString is shortcut if 
ever the struct implements a range interface!

3. If a range's element type (result type of front) is identical to the range's 
own type, writing runs into an infinite loop... This is well possible, for 
instance a textual type working like strings in high-level/dynamic languages (a 
character is a singleton string).

To solve these bugs, I guess the following changes would have to be done:
* The 3 ranges case must have 2 additional _negative_ constraints:
    ~ no toString defined on the type
    ~ (ElementType!T != T)
* The struct case must be split in 2 sub-cases:
    ~ use toString if defined
    ~ [else use range if defined, as given above]
    ~ if neither toString nore range, use T.stringof

I have tried to implement and test this modif, but ran into build errors 
(seemingly unrelated, about isTuple) I could not solve.

Now, I think it is worth wondering whether all these complications, only to 
have _default_ formatValue's for input ranges, is worth it at all. On one hand, 
in view of the analogy, it looks like a nice idea to have them expressed like 
arrays. On the other, when can this feature be useful?
An first issue comes up because there is no way, AFAIK, to tell apart inherited 
and explicite toString methods of classes: is(typeof(val.toString() == string)) 
is always true for a class. So that the range case would never be triggered for 
classes -- only for structs.
So, to use this feature, (1) the type must be a struct (2) which defines no 
toString (3) whch implements a range interface, and (4) the range's element 
type must not be the range type itself. In addition, the most sensible output 
form for it should be precisely the one of an array.
Note that unlike for structs, programmers cannot define custom forms of array 
output ;-) This is the reason why a default array format is so helpful -- but 
this reason does not exist for structs, thank to toString (and later writeTo).
If no default form exists for ranges, then in the rare cases where a programmer 
would implement a range interface on a struct _and_ need to re-create an 
array-like format for it, this takes a few lines in toString, for instance:
    string toString () {
        string[] contents = new string[this.elements.length]; 
        foreach (i,e ; this.elements)
            contents[i] = to!string(this.elements[i]);
        return format("[%s]", join(contents, ", "));
    }

As a conclusion, I would recommend to get rid of the (3) range cases in the set 
of formatValue templates. (This would directly restore correctness, I guess 
--showing that range cases where probably added later.)

I marked the bug(s) with keyword 'spec', as it depends on: how do we want
struct/class/range formatting semantics to be?

(1) There is at least a doc/comment error, namely for the struct case 
(commentted as AA instead). Also, the online doc does not hold template 
constraints, so that it is not possible to determine which one is selected in 
given situations.
-- -- -- -- -- -- --
vit esse estrany ☣

spir.wikidot.com

Reply via email to