diff --git a/const-faq.dd b/const-faq.dd new file mode 100644 index 0000000000..ae86a40ad3 --- /dev/null +++ b/const-faq.dd @@ -0,0 +1,261 @@ +Ddoc + +$(D_S const(FAQ), + + $(P D's const system is unique, and so there are a lot of + questions about it. + ) + + $(UL + + $(ITEMR const, Why does D have const?) + $(ITEMR principles, What principles drove the D const design?) + $(ITEMR transitive-const, What is $(I transitive const)?) + $(ITEMR head-const, What is $(I head const)?) + $(ITEMR tail-const, What is $(I tail const)?) + $(ITEMR logical-const, What is $(I logical const)?) + $(ITEMR readonly, Why not use $(I readonly) to mean read only view?) + $(ITEMR java-const, Why did Java reject const?) + $(ITEMR cpp-const, How does const differ in C++?) + $(ITEMR invariant-strings, Why are strings invariant?) + $(ITEMR const-parameters, Why aren't function parameters const by default?) + $(ITEMR static-members, Are static class members covered by transitive const?) + ) + +$(ITEM const, Why does D have const?) + + $(P People often express frustration with const and invariant + in D 2.0 and wonder if it is worth it. + ) + + $(OL + + $(LI It makes function interfaces more self-documenting. Without + transitive const, for all pointer/reference parameters one must rely on + the documentation (which is always missing, out of date, or wrong). Note + that without transitivity, C++ const is nearly useless for such + self-documentation, which is why C++ programmers tend to rely on + convention instead. + ) + + $(LI It makes for interfaces that can be relied upon, which becomes + increasingly important the more people that are involved with the code. + In other words, it scales very well. People who are involved with + projects with large teams of programmers say that lack of const + makes their lives difficult because they cannot rely on the compiler to + enforce convention. The larger the team, the worse it gets. Managing + APIs is critical to a large project - it's why BASIC doesn't scale (for + an extreme example). + ) + + $(LI Const transitivity makes for some interesting optimization + opportunities. The value of this has not been explored or exploited. + ) + + $(LI Here's the biggie. Points 1..3 are insignificant in comparison. The + future of programming will be multicore, multithreaded. Languages that + make it easy to program them will supplant languages that don't. + Transitive const is key to bringing D into this paradigm. The surge in + use of Haskell and Erlang is evidence of this coming trend (the killer + feature of those languages is they make it easy to do multiprogramming). + C++ cannot be retrofitted to supporting multiprogramming in a manner + that makes it accessible. D isn't there yet, but it will be, and + transitive const will be absolutely fundamental to making it work. + ) + ) + + $(P Of course, for writing single-threaded one man programs of + fairly modest size, const is not particularly useful. + And in D const can be effectively ignored by just not using it, or + by using D 1.0. The only place const is imposed is with the immutable + string type. + ) + +$(ITEM principles, What principles drove the D const design?) + + $(OL + $(LI It will be mathematically sound. That means there + are no legal escapes from it.) + $(LI Any type can be wrapped in a struct and the resulting + struct can still exhibit the same const behavior - in other + words, no magic behavior for certain types.) + $(LI Const behavior will be transitive.) + $(LI Const behavior for type T will be equivalent for all types T.) + ) + +$(ITEM transitive-const, What is $(I transitive const)?) + + $(P Transitive const means that once const is applied to a type, + it applies recursively to every sub-component of that type. Hence: + ) + +--- +const(int*)** p; +p += 1; // ok, p is mutable +*p += 1; // ok, *p is mutable +**p += 1; // error, **p is const +***p += 1; // error, ***p is const +--- + + $(P With transitivity, there is no way to have a + $(I const pointer to mutable int). + ) + + $(P C++ const is not transitive.) + +$(ITEM head-const, What is $(I head const)?) + + $(P Head const is where the const applies only to the component + of the type adjacent to the const. For example: + ) + +--- +headconst(int**) p; +--- + $(P would be read as p being a: $(I const pointer to mutable pointer + to mutable int.) D does not have head const (the $(CODE headconst) is + there just for illustrative purposes), but C++ const is + a head const system. + ) + +$(ITEM tail-const, What is $(I tail const)?) + + $(P Tail const is the complement of head const - everything reachable + from the const type is also const except for the top level. For + example: + ) + +--- +tailconst(int**) p; +--- + $(P would be read as p being a: $(I mutable pointer to const pointer + to const int.) Head const combined with tail const yields transitive + const. + D doesn't have $(CODE tailconst) (the keyword is there just for + illustrative purposes) as a distinct type constructor. + ) + +$(ITEM logical-const, What is $(I logical const)?) + + $(P $(I Logical const) refers to data that appears to be constant + to an observer, but is not actually const. An example would be + an object that does lazy evaluation:) + +--- +struct Foo { + mutable int len; + mutable bool len_done; + const char* str; + int length() + { if (!len_done) + { len = strlen(str); + len_done = true; + } + return len; + } + this(char* str) { this.str = str; } +} +const Foo f = Foo("hello"); +bar(f.length); +--- + + $(P The example evaluates $(CODE f.len) only if it is needed. + $(CODE Foo) is logically const, because to the observer of the object + its return values never change after construction. + The $(CODE mutable) qualifier says that even if an instance + of $(CODE Foo) is const, those fields can still change. + While C++ supports the notion of logical const, D does not, + and D does not have a $(CODE mutable) qualifier. + ) + + $(P The problem with logical const is that const is no longer + transitive. Not being transitive means there is the potential + for threading race conditions, and there is no way to determine + if an opaque const type has mutable members or not. + ) + + $(P Reference: + $(LINK2 http://www.linuxtopia.org/online_books/programming_books/thinking_in_c++/Chapter08_025.html, mutable: bitwise vs. logical const) + ) + +$(ITEM readonly, Why not use $(I readonly) to mean read only view?) + + $(P $(I Readonly) has a well established meaning in software to + mean ROM, or Read Only Memory that can never be changed. + For computers with hardware protection for memory pages, readonly + also means that the memory contents cannot be altered. + Using readonly in D to mean a read only view of memory that could + be altered by another alias or thread runs counter to this. + ) + +$(ITEM java-const, Why did Java reject const?) + + $(P $(LINK http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4211070) + ) + +$(ITEM cpp-const, How does const differ in C++?) + + $(P C++ has a const system that is closer to D's than any other + language, but it still has huge differences:) + + $(OL + $(LI const is not transitive) + $(LI no invariants) + $(LI const objects can have mutable members) + $(LI const can be legally cast away and the data modified) + $(LI $(CODE const T) and $(CODE T) are not always distinct types) + ) + +$(ITEM invariant-strings, Why are strings invariant?) + + $(P $(LINK2 http://dobbscodetalk.com/index.php?option=com_myblog&show=Invariant-Strings.html&Itemid=29, Invariant Strings) + ) + +$(ITEM const-parameters, Why aren't function parameters const by default?) + + $(P Since most (nearly all?) function parameters will not be modified, + it would seem to make sense to make them all const by default, + and one would have to specifically mark as mutable those that would + be changed. The problems with this are: + ) + + $(OL + + $(LI It would be a huge break from past D practice, and practice + in C, C++, Java, C#, etc.) + $(LI It would require a new keyword, say $(CODE mutable).) + $(LI And worst, it would make declarations inconsistent: +--- +void foo(int* p) { + int* q; + ... +} +--- + $(CODE p) points to const, and $(CODE q) points to mutable. + This kind of inconsistency leads to all sorts of mistakes. + It also makes it very hard to write generic code that deals with + types. + ) + ) + + $(P Using $(CODE in) can mitigate the ugliness of having to annotate + with $(CODE const):) +--- +void str_replace(in char[] haystack, in char[] needle); +--- + +$(ITEM static-members, Are static class members covered by transitive const?) + + $(P A static class member is part of the global state of a program, + not part of the state of an object. Thus, a class having a mutable + static member does not violate the transitive constness of an object + of that class. + ) + +) + +Macros: + TITLE=const(FAQ) + WIKI=constFAQ + ITEMR=$(LI $(LINK2 #$1, $+)) + ITEM=

$+

diff --git a/operatoroverloading.dd b/operatoroverloading.dd index d5caf6944b..d1a094ef6d 100644 --- a/operatoroverloading.dd +++ b/operatoroverloading.dd @@ -7,7 +7,19 @@ $(SPEC_S Operator Overloading, binary operators. No additional syntax is used. ) -

Unary Operator Overloading

+ $(UL + $(LI $(LINK2 #Unary, Unary Operator Overloading)) + $(LI $(LINK2 #Binary, Binary Operator Overloading)) + $(LI $(LINK2 #FunctionCall, Function Call Operator Overloading)) + $(LI $(LINK2 #Array, Array Operator Overloading)) + $(LI $(LINK2 #Assignment, Assignment Operator Overloading)) +$(V2 + $(LI $(LINK2 #Dot, Forwarding)) +) + $(LI $(LINK2 #Future, Future Directions)) + ) + +

Unary Operator Overloading

$(TABLE1 @@ -128,7 +140,7 @@ void test() } ------- -

Binary Operator Overloading

+

Binary Operator Overloading

$(TABLE1 @@ -375,34 +387,52 @@ int $(B opCmp)(Object o); $(P so that every class object has a $(CODE $(B opCmp)()). ) - $(P If a struct has no $(B opCmp)() function declared for it, attempting + $(P $(CODE $(B opCmp)) for structs works analogously to + $(CODE $(B opEquals)) for structs: + ) +------- +struct Pair +{ + int a, b; + int $(B opCmp)(Pair rhs) + { + if (a!=rhs.a) return a-rhs.a; + return b-rhs.b; + } +} +------- + + $(P If a struct has no $(CODE $(B opCmp)()) function declared for it, + attempting to compare two structs is an error. )

Rationale

- $(P The reason for having both $(B opEquals)() and $(B opCmp)() is - that:) + $(P The reason for having both $(CODE $(B opEquals)) and + $(CODE $(B opCmp)) is that:) $(UL $(LI Testing for equality can sometimes be a much more efficient operation than testing for less or greater than.) - $(LI Having an opCmp defined in Object makes it possible to + $(LI Having an $(CODE $(B opCmp)) defined in $(CODE Object) + makes it possible to make associative arrays work generically for classes.) $(LI For some objects, testing for less or greater makes no sense. - This is why Object.opCmp throws a runtime error. - opCmp must be overridden in each class for which comparison + This is why $(CODE Object.$(B opCmp)) throws a runtime error. + $(CODE $(B opCmp)) must be overridden in each class for which comparison makes sense.) ) $(P The parameter to $(B opEquals) and $(B opCmp) for class definitions must - be of type Object, rather than the type of the particular class, - in order to override the Object.$(B opEquals) and Object.$(B opCmp) + be of type $(CODE Object), rather than the type of the particular class, + in order to override the $(CODE Object.$(B opEquals)) and + $(CODE Object.$(B opCmp)) functions properly. ) -

Function Call Operator Overloading $(I f)()

+

Function Call Operator Overloading $(I f)()

$(P The function call operator, (), can be overloaded by declaring a function named $(B opCall): @@ -428,7 +458,7 @@ void test() were a function. ) -

Array Operator Overloading

+

Array Operator Overloading

Overloading Indexing $(I a)[$(I i)]

@@ -496,7 +526,7 @@ void test() } ------- -

Assignment Operator Overloading

+

Assignment Operator Overloading

$(P The assignment operator $(CODE =) can be overloaded if the lvalue is a struct $(V1 or class) aggregate, and $(CODE opAssign) @@ -522,7 +552,65 @@ opAssign(T, U = defaultValue, etc.) implicitly convertible to $(I A). ) -

Future Directions

+$(V2 +

Forwarding

+ + $(P Providing a struct or class member function $(CODE opDot) enables + the forwarding + of any names not found in the struct's scope to be forwarded + to the return type of the $(CODE opDot) function. In other words: + ) + +--- +struct T { + ... + S opDot() { ... } +} +T t; +... +t.m +--- + + $(P is rewritten as:) + +--- +t.opDot().m +--- + + $(P if m does not exist as a member of the struct T.) + + $(P The members .sizeof, .init, .offsetof, .alignof, .mangleof + and .stringof are not forwarded to $(CODE opDot).) + +--- +struct S { + int a, b, c; +} + +struct T { + S s; + int b = 7; + + S* opDot() { + return &s; // forwards to member s + } +} + +void main() { + T t; + t.a = 4; + t.b = 5; + t.c = 6; + assert(t.a == 4); + assert(t.b == 5); // T.b overrides S.b + assert(t.c == 6); + assert(t.s.b == 0); + assert(t.sizeof == 4*4); // sizeof T, not sizeof S +} +--- +) + +

Future Directions

$(P The operators $(CODE ! . && || ?:) and a few others will likely never be overloadable. diff --git a/statement.dd b/statement.dd index d80e9d0219..bc128d30c5 100644 --- a/statement.dd +++ b/statement.dd @@ -121,7 +121,7 @@ $(P Statements can be labeled. A label is an identifier that $(GRAMMAR $(I LabelledStatement): - $(I Identifier) ':' $(PSSEMI) + $(I Identifier) : $(PSSEMI) ) $(P @@ -205,15 +205,15 @@ $(I IfStatement): $(B if $(LPAREN)) $(I IfCondition) $(B $(RPAREN)) $(I ThenStatement) $(B if $(LPAREN)) $(I IfCondition) $(B $(RPAREN)) $(I ThenStatement) $(B else) $(I ElseStatement) -$(I IfCondition): +$(GNAME IfCondition): $(EXPRESSION) $(B auto) $(I Identifier) $(B =) $(EXPRESSION) $(I Declarator) $(B =) $(EXPRESSION) -$(I ThenStatement): +$(GNAME ThenStatement): $(PSSCOPE) -$(I ElseStatement): +$(GNAME ElseStatement): $(PSSCOPE) ) @@ -324,15 +324,15 @@ $(GRAMMAR $(I ForStatement): $(B for $(LPAREN))$(I Initialize) $(I Test)$(B ;) $(I Increment)$(B $(RPAREN)) $(PSSCOPE) -$(I Initialize): +$(GNAME Initialize): $(B ;) $(PS0) -$(I Test): +$(GNAME Test): $(I empty) $(EXPRESSION) -$(I Increment): +$(GNAME Increment): $(I empty) $(EXPRESSION) ) @@ -401,7 +401,7 @@ $(GNAME Foreach): $(B foreach) $(B foreach_reverse) -$(I ForeachTypeList): +$(GNAME ForeachTypeList): $(I ForeachType) $(I ForeachType) , $(I ForeachTypeList) @@ -411,7 +411,7 @@ $(GNAME ForeachType): $(B ref) $(I Identifier) $(I Identifier) -$(I Aggregate): +$(GNAME Aggregate): $(EXPRESSION) $(I Tuple) ) @@ -1144,18 +1144,21 @@ $(I TryStatement): $(B try) $(PSSCOPE) $(I Catches) $(I FinallyStatement) $(B try) $(PSSCOPE) $(I FinallyStatement) -$(I Catches): +$(GNAME Catches): $(I LastCatch) $(I Catch) $(I Catch) $(I Catches) -$(I LastCatch): +$(GNAME LastCatch): $(B catch) $(PS0) -$(I Catch): +$(GNAME Catch): $(B catch $(LPAREN)) $(I CatchParameter) $(B $(RPAREN)) $(PS0) -$(I FinallyStatement): +$(GNAME CatchParameter): + $(I BasicType) $(I Identifier) + +$(GNAME FinallyStatement): $(B finally) $(PS0) ) @@ -1365,11 +1368,11 @@ $(I VolatileStatement): Inline assembler is supported with the asm statement: $(GRAMMAR -$(I AsmStatement): +$(GNAME AsmStatement): $(B asm { }) $(B asm {) $(I AsmInstructionList) $(B }) -$(I AsmInstructionList): +$(GNAME AsmInstructionList): $(I AsmInstruction) $(B ;) $(I AsmInstruction) $(B ;) $(I AsmInstructionList) ) @@ -1492,10 +1495,10 @@ $(GRAMMAR $(I ForeachRangeStatement): $(GLINK Foreach) $(LPAREN)$(GLINK ForeachType)$(B ;) $(I LwrExpression) $(B ..) $(I UprExpression) $(B $(RPAREN)) $(PSSCOPE) -$(I LwrExpression): +$(GNAME LwrExpression): $(EXPRESSION) -$(I UprExpression): +$(GNAME UprExpression): $(EXPRESSION) )