Skip to content

Conversation

quickfur
Copy link
Member

@quickfur quickfur commented Nov 4, 2014

Fixes: https://issues.dlang.org/show_bug.cgi?id=9970

This is an initial stab at documenting the difference between type constructors and storage classes. Please chime in with any additions, omissions, inaccuracies, etc., that I've probably missed. :-)

@JinShil
Copy link
Contributor

JinShil commented Nov 6, 2014

IMO this is informative and well-written. I also couldn't find any DDoc syntax errors.

@schuetzm
Copy link
Contributor

schuetzm commented Nov 6, 2014

Hmm... playing the devil's advocate: So what is the difference between const as storage class and const as type constructor? Why isn't it only an implementation detail?

@quickfur
Copy link
Member Author

quickfur commented Nov 6, 2014

This is where I need some help from the core dmd devs to clarify things... the only difference I know of between const as a storage class and const as a type constructor is the following:

struct S {
    // Type constructor
    const(int) method() { ... }

    // Storage class
    const int method() { ... }  // same as `int method() const`, NOT `const(int) method()`
}

But as for the difference in variable declarations, I honestly have no idea:

const int x; // storage class, but that also implies typeof(x) == const(int)
const(int) x; // type constructor
// what's the difference??

@jmdavis
Copy link
Member

jmdavis commented Nov 6, 2014

IIRC, in a discussion in the newsgroup not too long ago, Walter and Dicebot were discusssing it, and Dicebot was arguing that there really isn't any difference, but I don't remember what thread that was. And even if there is a difference for member functions, if we could just get it to become illegal to put const on the left like that, then that difference would go away.

@quickfur
Copy link
Member Author

quickfur commented Nov 7, 2014

If it were up to me, I'd say all const-qualified types should be written with parentheses const(T), then we wouldn't have this ambiguity.

@jmdavis
Copy link
Member

jmdavis commented Nov 7, 2014

If it were up to me, I'd say all const-qualified types should be written with parentheses const(T), then we wouldn't have this ambiguity.

That would be annoying in other ways, but it wouldn't really solve the const on the left problem anyway, because too many folks (especially newbies) would end up assuming that the const applied to the return type rather than the function, especially when D is the only language (AFAIK) to use parens with const.

@quickfur
Copy link
Member Author

quickfur commented Nov 7, 2014

OK, so let's have both: storage class const must be on the right, and type constructors require parentheses. :-)

@jmdavis
Copy link
Member

jmdavis commented Nov 7, 2014

OK, so let's have both: storage class const must be on the right, and type constructors require parentheses. :-)

LOL. Honestly, I would find it very annoying if I always had to use parens with const. I basically only use them when I have to; otherwise, I don't bother, which usually means that I only have to use them with return types and arrays (and not even always with arrays).

@quickfur
Copy link
Member Author

quickfur commented Nov 7, 2014

The reason I advocate parentheses is because of the subtle but important distinction between const(T[]) and const(T)[].

@MartinNowak
Copy link
Member

Wasn't Andrei recently saying we should talk about type qualifiers not type constructors, because type constructors also include things as T*, T[], or T[K].

@jmdavis
Copy link
Member

jmdavis commented Nov 7, 2014

The reason I advocate parentheses is because of the subtle but important distinction between const(T[]) and const(T)[].

Well, I don't see how forcing everyone to use parens would help with that. If anything, it makes it worse, because then there are more cases where you have to figure out exactly what the parens are around.

Wasn't Andrei recently saying we should talk about type qualifiers not type constructors, because type constructors also include things as T*, T[], or T[K].

TDPL certainly seems to favor the term type qualifier, so that wouldn't surprise me. I'm afraid that I'm not as educated as I should be on the differences.

@quickfur
Copy link
Member Author

quickfur commented Nov 7, 2014

Forcing people to use parens will also force them to think more carefully about whether they really want const(T[]) or const(T)[], which means it's more likely they'll get it right than if they blindly wrote const T[] for everything, even when it's wrong.

@jmdavis
Copy link
Member

jmdavis commented Nov 7, 2014

Forcing people to use parens will also force them to think more carefully about whether they really want const(T[]) or const(T)[], which means it's more likely they'll get it right than if they blindly wrote const T[] for everything, even when it's wrong.

Maybe. The type system would generally just result in compilation errors if they got it wrong, and personally, I've never had a problem with it, but I don't know about the average D programmer.

@quickfur
Copy link
Member Author

quickfur commented Nov 7, 2014

@quickfur
Copy link
Member Author

quickfur commented Nov 7, 2014

@jmdavis It often leads to frustration, even if the compiler catches it:

void doSomething(string x) {
    const char[] slice = x[];
    slice.popFront(); // fails, argh
}
struct S {
    int[] data;
    int find(int key) const {
        const char[] slice = data[];
        foreach (x; slice) { // fails, argh
            ...
        }
    }
}

Worse yet, if somebody accidentally gives the wrong type to a library API, you're screwed:

// library.d
struct Wrapper(T) if (is(T : U[], U)) {
    const T[] impl; // oops
    alias impl this;
}
// usercode.d
import library;
immutable int arr[];
foreach (x; Wrapper!(typeof(arr))(arr)) { // fails, argh
}

Basically, it's an all-round annoyance, even if it's nothing drastically nasty. I vote for mandatory parens. Bring on the lispism. :-P

@jmdavis
Copy link
Member

jmdavis commented Nov 7, 2014

Basically, it's an all-round annoyance, even if it's nothing drastically nasty. I vote for mandatory parens. Bring on the lispism. :-P

Well, I think that we're just going to have to agree to disagree on that one.

@MartinNowak
Copy link
Member

TDPL certainly seems to favor the term type qualifier, so that wouldn't surprise me. I'm afraid that I'm not as educated as I should be on the differences.

Issue 13671 - http://dlang.org/const3.html incorrectly calls type qualifiers "type constructors"

@MartinNowak
Copy link
Member

Can you please replace the term type constructor with type qualifier?

@quickfur
Copy link
Member Author

Done. Any other suggestions? The current PR still looks a bit incomplete, as things like the difference between const(int) x; and const int x; are still not that clear to me, so I have avoided addressing that issue.

@rainers
Copy link
Member

rainers commented Nov 14, 2014

Maybe I missed important parts of the discussion (and I don't actually want to be dragged deep into it ;-)), but even with the description in this PR, I don't get what makes "const" a storage type.

const int x; is the same as writing const { int x; } and similar for functions. In this case const is an AttributeSpecifier according to the grammar. The doc says: "The const attribute changes the type of the declared symbol from T to const(T), where T is the type specified (or inferred) for the introduced symbol in the absence of const." [http://dlang.org/attribute.html#const]. That's slightly wrong for functions, because it is actually a type qualifier on this for member functions, and forbidden for free functions.

The type qualifier has an influence on where a variable is stored, but that is not influenced by the way the modifier is written. In addition, storage is determined by the scope of the declaration and whether an initializer is given.

@quickfur
Copy link
Member Author

OK, so looks like the real source of the confusion is the visual confusion between:

const int x;

and

const int func();

The former is equivalent to const(int) x, whereas the latter is actually int func() const. Which makes one wonder how to write const(int) func() const? Like this: const const func()?

@rainers
Copy link
Member

rainers commented Nov 14, 2014

The former is equivalent to const(int) x, whereas the latter is actually int func() const.

Yes.

Which makes one wonder how to write const(int) func() const?

That would be my prefered style.

Like this: const const func()?

that produces redundant attribute 'const'. You can write it as const const(int) func().

@rainers
Copy link
Member

rainers commented Nov 14, 2014

Looking at the grammar again, StorageClass is a misnomer and should be replaced with Attribute. If you follow the grammar starting at AttributeSpecifier, you are supposed to parse something like "Attributes_opt StorageClassses_opt BasicType ..." with Attribute and StorageClass being identical.

MartinNowak added a commit that referenced this pull request Nov 21, 2014
Issue 9970: Document difference between type constructors & storage classes.
@MartinNowak MartinNowak merged commit a136c0b into dlang:master Nov 21, 2014
@MartinNowak
Copy link
Member

Thanks

@quickfur quickfur deleted the issue9970 branch November 21, 2014 17:16
@ghost
Copy link

ghost commented Nov 23, 2014

It would have been nice if from the get-go we had the syntax const(this). Then there would be no confusion at all and you could place it anywhere you want.

@quickfur
Copy link
Member Author

Oh?? Do we actually support this syntax right now, or is it just a wishlist?

@ghost
Copy link

ghost commented Nov 23, 2014

A wish for a time-travel machine. :)

@quickfur
Copy link
Member Author

Haha, OK. Hmm... what are the chances that this syntax can be added as an alternative syntax, and eventually become the recommended syntax, and the current syntax deprecated?

@ghost
Copy link

ghost commented Nov 24, 2014

It all depends on whether Walter thinks it's worth it. Personally I like syntax that makes things clear. I'd even love some form of implements keyword when a method isn't overriding but implementing an interface method. It's really hard to tell what's part of an API without looking explicitly into the base classes. But I digress.. :)

@MartinNowak
Copy link
Member

Override would already be nice for for this.
https://issues.dlang.org/show_bug.cgi?id=2525

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants