www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Few things

reply bearophile <bearophileHUGS mailas.com> writes:
Hello, this is my second post on digitalmars newsgroups (the first one is on
digitalmars.D.learn). Here are some ideas, suggestions (and some problems too)
I have found. I am currently using dmd 1.020 on Windows. Probably some of them
are useless (maybe because already present, in the same or different form), or
already discussed/refused, or even plain stupid, but you may find something
interesting too among them.


1) This is quite important for me: dmd may look for the needed modules by
itself, starting from the current directory and package directories (the -I
parameter is useful still for more complex situations), and the exploring the
directories in the "path" variable. So instead of:

dmd importer.d amodule1.d amodule2.d ...

To compile/run you just need:

dmd importer.d
dmd -run importer.d

So the programmer can avoid giving the module names two times, once inside the
code and once again to the compiler. Later I have found that the good "bud"
utility does that and quite more, but I belive the compiler can have that basic
capability of looking for modules by itself.

-------------------------

2) dmd can start looking for the files read by the "import expressions" from
the current directory (where the main module is), so in most situations the -J
flag isn't necessary anymore.

-------------------------

3) I think it may be better to import "statically" by default (as in python, in
that language people suggest to avoid the "from amodule import *" that means
import all names from amodule).

-------------------------

4) Python defines < <= == != >= > among dicts (AAs) too:

 {1:2, 2:3} == {1:2, 2:3}
True
 {1:3, 2:3} == {1:2, 2:3}
False
 {1:2, 2:3} < {1:2, 2:3}
False
 {1:2, 2:3} > {1:2, 2:3}
False
 {1:2, 2:3, 3:1} > {1:2, 2:3}
True
 {1:2, 2:3, 3:1} > {1:2, 2:3, 4:1}
False
 {1:2, 2:3, 3:1} < {1:2, 2:3, 4:1}
True It seems not even the quite useful opEquals among AAs is defined yet in dmd V1.015: assert(['a':2, 'b':3] == ['a':2, 'b':3]); How I have implemented it: bool equalAA(TyK1, TyV1, TyK2, TyV2)(TyV1[TyK1] aa1, TyV2[TyK2] aa2) { static if( !is(TyK1 == TyK2) || !is(TyV1 == TyV2) ) return false; else { if (aa1.length != aa2.length) return false; foreach(k, v; aa1) { auto k_in_aa2 = k in aa2; if (!k_in_aa2 || (*k_in_aa2 != v)) return false; } return true; } } Usecase: I use it inside unittests to test the correctness of functions that return an AA, comparing their result with the known correct results. ------------------------- 5) Iside the unit tests I'd like to use something else beside the assert() like the fail(): fails(something, someException1) fails(something, Excep1, Excep2, ...) (Or "raises" instead of "fails"). So instead of code like: bool okay = false; try foo(-10); catch(NegativeException e) okay = true; assert(okay); I can use something like: fails(foo(-10), NegativeException); So far I've managed to create the following, but its usage is far from nice, it needs (maybe you can improve it): assert(Raises!(...)(...)) bool Raises(TyExceptions...)(void delegate() deleg) { try deleg(); catch(Exception e) { foreach(TyExc; TyExceptions) if (cast(TyExc)e !is null) return true; return false; } return (!TyExceptions.length); } ------------------------- 6) It can be useful a way to create an empty associative array with something like: new int[char[]](); I have succed creating something similar, but I think a standard built-in way is better: template AA(KeyType, ValueType) { const ValueType[KeyType] AA = AA_impl!(KeyType, ValueType).res; } template AA_impl(KeyType, ValueType) { ValueType[KeyType] result; const ValueType[KeyType] res = result; } Usage: AA!(char[], int) ------------------------- 7) From the FAQ: >Many people have asked for a requirement that there be a break between cases in a switch statement, that C's behavior of silently falling through is the cause of many bugs. The reason D doesn't change this is for the same reason that integral promotion rules and operator precedence rules were kept the same - to make code that looks the same as in C operate the same. If it had subtly different semantics, it will cause frustratingly subtle bugs.< I agree with both points of view. My idea: calling this statement differently (like caseof) instead of "switch" (like in Pascal), so you can change its semantics too, removing the falling through (you may use the Pascal semantic too). ------------------------- 8) From the docs: >If the KeyType is a struct type, a default mechanism is used to compute the hash and comparisons of it based on the binary data within the struct value. A custom mechanism can be used by providing the following functions as struct members:< I think structs can have a default way to sort them too, lexicographically, that can be replaced by a custom mechanism when needed. How I have implemented it: int scmp(TyStruct1, TyStruct2)(TyStruct1 s1, TyStruct2 s2) { static if (s1.tupleof.length < s2.tupleof.length) { foreach(i, field; s1.tupleof) { if (field < s2.tupleof[i]) return -1; else if (field > s2.tupleof[i]) return 1; } return -1; } else { foreach(i, field; s2.tupleof) { if (field < s1.tupleof[i]) return 1; else if (field > s1.tupleof[i]) return -1; } static if (s1.tupleof.length == s2.tupleof.length) return 0; else return 1; } } How I use it, to shorten the code I use to sort structs: struct P1 { char[] k; int v; int opCmp(P1 other) { return scmp(this, other); } } I think dmd can do something like that by default. ------------------------- 9) Often 90% of the lines of a program don't need to run at max speed or to use as little memory as possible, for such lines a higher-level kind of programming is the best thing. For the other 10% of lines, D allows a lower-level programming style, allowing assembly too. For that 90% of code it may be useful to add an *optional* parameter to the sort property/methods of arrays, you may call it 'key' (see the same parameter of Python V.2.4+ sort/sorted), it's a comparison function that takes a value of the array and return another value that is compared to sort the original values. (Note: CPython has a *really* optimized C sort routine, called Timsort (inside listobject), D may just copy it if some tests show it's faster & better). (There is an almost-free-standing version of Timsort too that can be found online.) ------------------------- 10) I think few useful properties/methods can be added to the built-in AAs: - aa.clear() to remove all key/val from the AA. - aa.dup, to create a copy of the AA. - aa.sort, that compulsively takes a 'key' function (that takes two arguments, key and value) and returns the sorted array of the keys. Here is a simple example of such sorting: TyKey[] sortedAA(TyKey, TyVal, TyFun)(TyVal[TyKey] aa, TyFun key) { struct Pair { TyKey k; ReturnType!(TyFun) key_kv; int opCmp(Pair otherPair) { if (key_kv == otherPair.key_kv) return 0; return (key_kv < otherPair.key_kv) ? -1 : 1; } } Pair[] pairs; pairs.length = aa.length; uint i = 0; foreach(k, v; aa) { pairs[i] = Pair(k, key(k, v)); i++; } TyKey[] result; result.length = aa.length; foreach(ii, p; pairs.sort) result[ii] = p.k; return result; } You can use it with any key, like: TyV Vgetter(TyK, TyV)(TyK k, TyV v) { return v; } For the 10% of the lines of the program that need max speed or low memory usage, you can write sorting code the usual lower-level way. ------------------------- 11) I think std.string can have an iterable split too, similar to this one. For big input strings it's *much* faster and requires much less memory than the usual split (all Python is shifting from functions that return arrays to lazy iterators, that are often faster and need less memory): Xsplitarray!(TyElem) xsplitarray(TyElem)(TyElem[] items, TyElem delimiter) { return new Xsplitarray!(TyElem)(items, delimiter); } class Xsplitarray(TyElem) { TyElem[] items, part; TyElem delimiter; this(TyElem[] initems, TyElem indelimiter) { items = initems; delimiter = indelimiter; } int opApply(int delegate(ref TyElem[]) dg) { size_t i, j; int result; while (j < items.length) for (; j<items.length; j++) if (items[j] == delimiter) { part = items[i .. j]; result = dg(part); if (result) goto END; // ugly i = j = j + 1; break; } if (i <= items.length) { part = items[i .. length]; dg(part); } END: return 0; } } ------------------------- 12) From the docs:>Pointers in D can be broadly divided into two categories: those that point to garbage collected memory, and those that do not.< GC pointers don't support some operations, so why not define two kinds of pointers (gcpointer?), so the compiler can raise an error when the code tries to do one of the many illegal operations (= that produces undefined behavior) on GC pointers? (If it's needed a cast() may be used to convert a GC pointer to a normal pointer and vice versa). ------------------------- 13) Coverage processing: numbers can become misaligned in very long loops that execute some lines lot of times. So I suggest three changes: ============ instead of "0000000" because among the other numbers my eyes spot a sequence of equal signs better than a sequence of zeros, automatic indenting management, and the ability to divide numbers by 1000 or 1'000'000. I use this little (a bit cryptic) Python script to do such processing (but I'd like dmd to do something similar by itself): filename = "fannkuch3" #remove_last = None # then numbers shown are the original ones #remove_last = -3 # then numbers shown are in thousands remove_last = -6 # then numbers shown are in millions lines = file(filename+ ".lst").readlines()[:-2] parts = [line.split("|", 1) for line in lines] n = max(len(p1) for p1, p2 in parts) # len of the maximum number for p1, p2 in parts: p1s = p1.strip() p2b = "|" + p2.rstrip() if p1s: if p1s == "0000000": print ("=" * (n + remove_last)) + p2b else: print p1s.rjust(n)[:remove_last] + p2b else: print (" " * (n + remove_last)) + p2b ------------------------- 14) Maybe it can be useful to optionally associate a unittest to the name of the function/class/method tested. Possible syntax: unittest (name) { } Or: unittest name { } This may be used by IDEs to manage test-driven development in a smarter way. I don't know how the compiler itself can use that information. ------------------------- 15) On windows, expecially for newbies I think it can be positive to have a single zip with all necessary to use D (with dmd, dmc, bud and maybe dfl too). ------------------------- 16) I think std.string.chop and std.string.chomp have too much similar names, so one of such functions can be renamed (I think the function that removes the last char can be removed, it's not much useful. While the other function that removes the optionally present newline is quite useful). ------------------------- 17) It may be good if in dmd the -run parameter can be positioned anywhere in the command line. ------------------------- 18) In Python I find rather useful the built-in ** infix operator (it works among integers or floating point numbers):
 2 ** 3
8
 2 ** 0.5
1.4142135623730951
 -2 ** -2.5
-0.17677669529663689 std.math.pow is less useful when both arguments are integral. ------------------------- 19) The dmd compiler can tell what variables aren't used (Delphi Pascal does this) or the ones that aren't modified after the initialization... (that is that are essentially unused). Hugs, bearophile
Aug 01 2007
next sibling parent reply Sean Kelly <sean f4.ca> writes:
bearophile wrote:
 Hello, this is my second post on digitalmars newsgroups (the first one is on
digitalmars.D.learn). Here are some ideas, suggestions (and some problems too)
I have found. I am currently using dmd 1.020 on Windows. Probably some of them
are useless (maybe because already present, in the same or different form), or
already discussed/refused, or even plain stupid, but you may find something
interesting too among them.
 
 
 1) This is quite important for me: dmd may look for the needed modules by
itself, starting from the current directory and package directories (the -I
parameter is useful still for more complex situations), and the exploring the
directories in the "path" variable. So instead of:
 
 dmd importer.d amodule1.d amodule2.d ...
 
 To compile/run you just need:
 
 dmd importer.d
 dmd -run importer.d
 
 So the programmer can avoid giving the module names two times, once inside the
code and once again to the compiler. Later I have found that the good "bud"
utility does that and quite more, but I belive the compiler can have that basic
capability of looking for modules by itself.
This has been requested before and I think it would be a useful feature to have. Build tools like Bud could still add value by supporting custom build scripts, packaging, etc.
 -------------------------
 
 2) dmd can start looking for the files read by the "import expressions" from
the current directory (where the main module is), so in most situations the -J
flag isn't necessary anymore.
I think the -J flag was added for security purposes so the import feature couldn't be subverted. But I agree that it's not useful to the average programmer.
 -------------------------
 
 3) I think it may be better to import "statically" by default (as in python,
in that language people suggest to avoid the "from amodule import *" that means
import all names from amodule).
I think private import by default is enough, though the symbol lookup rules complicate things somewhat for library design. It would be preferable if private symbols weren't visible to other modules.
 -------------------------
 
 4) Python defines < <= == != >= > among dicts (AAs) too:
 
 {1:2, 2:3} == {1:2, 2:3}
True
 {1:3, 2:3} == {1:2, 2:3}
False
 {1:2, 2:3} < {1:2, 2:3}
False
 {1:2, 2:3} > {1:2, 2:3}
False
 {1:2, 2:3, 3:1} > {1:2, 2:3}
True
 {1:2, 2:3, 3:1} > {1:2, 2:3, 4:1}
False
 {1:2, 2:3, 3:1} < {1:2, 2:3, 4:1}
True It seems not even the quite useful opEquals among AAs is defined yet in dmd V1.015: assert(['a':2, 'b':3] == ['a':2, 'b':3]);
Seems handy, and more consistent than the current behavior.
 -------------------------
 
 5) Iside the unit tests I'd like to use something else beside the assert()
like the fail():
You can do this with Tango if you define a custom unit tester.
 -------------------------
 
 6) It can be useful a way to create an empty associative array with something
like:
 new int[char[]]();
What is the use for this over initializing to null?
 -------------------------
 
 7) From the FAQ: >Many people have asked for a requirement that there be a
break between cases in a switch statement, that C's behavior of silently
falling through is the cause of many bugs. The reason D doesn't change this is
for the same reason that integral promotion rules and operator precedence rules
were kept the same - to make code that looks the same as in C operate the same.
If it had subtly different semantics, it will cause frustratingly subtle bugs.<
 
 I agree with both points of view. My idea: calling this statement differently
(like caseof) instead of "switch" (like in Pascal), so you can change its
semantics too, removing the falling through (you may use the Pascal semantic
too).
It's a good idea, but I actually like the fall-through behavior. I use it regularly.
 -------------------------
 
 9) Often 90% of the lines of a program don't need to run at max speed or to
use as little memory as possible, for such lines a higher-level kind of
programming is the best thing. For the other 10% of lines, D allows a
lower-level programming style, allowing assembly too.
 For that 90% of code it may be useful to add an *optional* parameter to the
sort property/methods of arrays, you may call it 'key' (see the same parameter
of Python V.2.4+ sort/sorted), it's a comparison function that takes a value of
the array and return another value that is compared to sort the original values.
 (Note: CPython has a *really* optimized C sort routine, called Timsort (inside
listobject), D may just copy it if some tests show it's faster & better).
(There is an almost-free-standing version of Timsort too that can be found
online.)
I'm not sure if this solves the same problem, but Tango offers a sort routine that takes a predicate. It can be called as myArray.sort(&cmp) in most cases, making it look much like a built-in feature.
 -------------------------
 
 10) I think few useful properties/methods can be added to the built-in AAs:
 - aa.clear() to remove all key/val from the AA.
 - aa.dup, to create a copy of the AA.
 - aa.sort, that compulsively takes a 'key' function (that takes two arguments,
key and value) and returns the sorted array of the keys. Here is a simple
example of such sorting:
All sound useful, though it may be confusing to have AA.sort behave differently than array sort.
 -------------------------
 
 12) From the docs:>Pointers in D can be broadly divided into two categories:
those that point to garbage collected memory, and those that do not.<
 GC pointers don't support some operations, so why not define two kinds of
pointers (gcpointer?), so the compiler can raise an error when the code tries
to do one of the many illegal operations (= that produces undefined behavior)
on GC pointers? (If it's needed a cast() may be used to convert a GC pointer to
a normal pointer and vice versa).
I'm not sure I understand. Could you explain this further? Sean
Aug 01 2007
parent renoX <renosky free.fr> writes:
Sean Kelly a écrit :
 bearophile wrote:
 4) Python defines < <= == != >= > among dicts (AAs) too:

 {1:2, 2:3} == {1:2, 2:3}
True
 {1:3, 2:3} == {1:2, 2:3}
False
 {1:2, 2:3} < {1:2, 2:3}
False
 {1:2, 2:3} > {1:2, 2:3}
False
 {1:2, 2:3, 3:1} > {1:2, 2:3}
True
 {1:2, 2:3, 3:1} > {1:2, 2:3, 4:1}
False
 {1:2, 2:3, 3:1} < {1:2, 2:3, 4:1}
True It seems not even the quite useful opEquals among AAs is defined yet in dmd V1.015: assert(['a':2, 'b':3] == ['a':2, 'b':3]);
Seems handy, and more consistent than the current behavior.
While I agree with ==, I'm a bit puzzled by '<': is-it always possible to order keys and values? And if it isn't possible what will happen, a compile time error?
 7) From the FAQ: >Many people have asked for a requirement that there 
 be a break between cases in a switch statement, that C's behavior of 
 silently falling through is the cause of many bugs. The reason D 
 doesn't change this is for the same reason that integral promotion 
 rules and operator precedence rules were kept the same - to make code 
 that looks the same as in C operate the same. If it had subtly 
 different semantics, it will cause frustratingly subtle bugs.<

 I agree with both points of view. My idea: calling this statement 
 differently (like caseof) instead of "switch" (like in Pascal), so you 
 can change its semantics too, removing the falling through (you may 
 use the Pascal semantic too).
It's a good idea, but I actually like the fall-through behavior. I use it regularly.
Sure, fall-through behaviour is useful sometimes, but it shouldn't be the default.. Pascal semantic isn't good enough as it didn't have a way to allow fall-through but this could be added.. renoX
Aug 02 2007
prev sibling next sibling parent reply BCS <BCS pathlink.com> writes:
bearophile wrote:
 Hello, this is my second post on digitalmars newsgroups (the first one is on
digitalmars.D.learn). Here are some ideas, suggestions (and some problems too)
I have found. I am currently using dmd 1.020 on Windows. Probably some of them
are useless (maybe because already present, in the same or different form), or
already discussed/refused, or even plain stupid, but you may find something
interesting too among them.
 
"I bid you wv'elcome" --Bela Lugos'D
 
 1) ...
 To compile/run you just need:
 
 dmd importer.d
 dmd -run importer.d
 
shouldn't that be on line: dmd importer.d -run or am I misunderstanding?
 So the programmer can avoid giving the module names two times, once inside the
code and once again to the compiler. Later I have found that the good "bud"
utility does that and quite more, but I belive the compiler can have that basic
capability of looking for modules by itself.
 
 -------------------------
 
 5) Inside the unit tests I'd like to use something else beside the assert()
like the fail():
 
you should known what will be thrown and the function can do the assert, so I'd go with this void Raises(TyExceptions)(lazy void act, lazy char[] msg) { try act(); catch(TyException e) return; assert(false, msg()); } Raises!(MyExp)(someFunc(), "error "~message() );
 -------------------------
 
 6) It can be useful a way to create an empty associative array with something
like:
 new int[char[]]();
 
assigning null to an aa results in an empty aa, I'm not sure if it clear other references to it though.
 -------------------------
 
 7) From the FAQ: >Many people have asked for a requirement that there be a
break between cases in a switch statement, that C's behavior of silently
falling through is the cause of many bugs. The reason D doesn't change this is
for the same reason that integral promotion rules and operator precedence rules
were kept the same - to make code that looks the same as in C operate the same.
If it had subtly different semantics, it will cause frustratingly subtle bugs.<
 
 I agree with both points of view. My idea: calling this statement differently
(like caseof) instead of "switch" (like in Pascal), so you can change its
semantics too, removing the falling through (you may use the Pascal semantic
too).
 
fall thought case is of a lot of use in generated code and a number of other cases. I have a templated parser that benefits tremendously from it.
 -------------------------
 
 14) Maybe it can be useful to optionally associate a unittest to the name of
the function/class/method tested. Possible syntax:
 unittest (name) { }
 Or:
 unittest name { }
 This may be used by IDEs to manage test-driven development in a smarter way. I
don't know how the compiler itself can use that information.
you can just put it in the class class C { unittest { } }
  
 -------------------------
 
 15) On windows, expecially for newbies I think it can be positive to have a
single zip with all necessary to use D (with dmd, dmc, bud and maybe dfl too).
ditto for dmd + dmc, there might be some issues for including 3rd party aps though
 
 -------------------------
 
> 17) It may be good if in dmd the -run parameter can be positioned anywhere in the command line. -run is at the end because all args after it are passed to the program, it's used a a delimiter
Aug 01 2007
next sibling parent Pragma <ericanderton yahoo.removeme.com> writes:
BCS wrote:

 
 "I bid you wv'elcome" --Bela Lugos'D
Thanks, now I'll have Bauhaus stuck in my head all afternoon. :( -- - EricAnderton at yahoo
Aug 01 2007
prev sibling parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
BCS wrote:
 -------------------------

 6) It can be useful a way to create an empty associative array with 
 something like:
 new int[char[]]();
assigning null to an aa results in an empty aa, I'm not sure if it clear other references to it though.
Are you sure of that? It seems that at least for AA's, empty arrays are different than null arrays: int[char[]] b; writefln(b is null); // true writefln(b == null); // true b["hello"] = 3; b.remove("hello"); writefln(b); // empty AA writefln(b is null); // false writefln(b == null); // false (note this line in particular) b = null; writefln(b is null); // true writefln(b == null); // true -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Aug 02 2007
parent reply Sean Kelly <sean f4.ca> writes:
Bruno Medeiros wrote:
 BCS wrote:
 -------------------------

 6) It can be useful a way to create an empty associative array with 
 something like:
 new int[char[]]();
assigning null to an aa results in an empty aa, I'm not sure if it clear other references to it though.
Are you sure of that? It seems that at least for AA's, empty arrays are different than null arrays: int[char[]] b; writefln(b is null); // true writefln(b == null); // true
Huh. So equality comparisons with null work for AAs but crash with objects? Sean
Aug 02 2007
parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Sean Kelly wrote:
 Bruno Medeiros wrote:
 BCS wrote:
 -------------------------

 6) It can be useful a way to create an empty associative array with 
 something like:
 new int[char[]]();
assigning null to an aa results in an empty aa, I'm not sure if it clear other references to it though.
Are you sure of that? It seems that at least for AA's, empty arrays are different than null arrays: int[char[]] b; writefln(b is null); // true writefln(b == null); // true
Huh. So equality comparisons with null work for AAs but crash with objects? Sean
Seems so, just as you don't need to allocate an AA to use it, it is done automatically if the AA is null. -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Aug 03 2007
prev sibling next sibling parent reply Kirk McDonald <kirklin.mcdonald gmail.com> writes:
bearophile wrote:
 Hello, this is my second post on digitalmars newsgroups (the first
 one is on digitalmars.D.learn). Here are some ideas, suggestions (and
 some problems too) I have found. I am currently using dmd 1.020 on
 Windows. Probably some of them are useless (maybe because already
 present, in the same or different form), or already
 discussed/refused, or even plain stupid, but you may find something
 interesting too among them.
 
Greetings! (I rewrapped your post's linebreaks, but it seems this destroyed your code formatting. Oops.)
 
 1) This is quite important for me: dmd may look for the needed
 modules by itself, starting from the current directory and package
 directories (the -I parameter is useful still for more complex
 situations), and the exploring the directories in the "path"
 variable. So instead of:
 
 dmd importer.d amodule1.d amodule2.d ...
 
 To compile/run you just need:
 
 dmd importer.d dmd -run importer.d
 
 So the programmer can avoid giving the module names two times, once
 inside the code and once again to the compiler. Later I have found
 that the good "bud" utility does that and quite more, but I belive
 the compiler can have that basic capability of looking for modules by
 itself.
 
I also suggest looking at DSSS: http://www.dsource.org/projects/dsss/ The rebuild utility is part of dsss. Rebuild is very much like bud, but is more actively maintained. I also prefer its treatment of object files to bud's. The issue of bundling bud-like functionality into dmd has been brought up before, but I don't think Walter is all that keen on the idea.
 -------------------------
 
 3) I think it may be better to import "statically" by default (as in
 python, in that language people suggest to avoid the "from amodule
 import *" that means import all names from amodule).
 
Personally, I almost always try to use selective imports in D: import std.stdio : writefln; But maybe that's my Python background. :-) There was a massive thread on this newsgroup just before static, selective, and renaming imports were added. These were indeed inspired by Python's import semantics, but making everything a static import by default would have broken too much code, and was not deemed worth it. (However, imports were changed from public to private by default at that time.) A curious feature of D's imports is that with a regular "import foo;" you can still access the contents of foo by their fully-qualified name. This differs from Python's "from foo import *" in that, though all these imported names have been introduced into the namespace, you can still get at them if they have been shadowed by another name. Combined with the fact that someone importing your module will not get those imported names (since imports are private), the situation is just fine.
 -------------------------
 
 6) It can be useful a way to create an empty associative array with
 something like: new int[char[]]();
 
 I have succed creating something similar, but I think a standard
 built-in way is better:
 
 template AA(KeyType, ValueType) { const ValueType[KeyType] AA =
 AA_impl!(KeyType, ValueType).res; } template AA_impl(KeyType,
 ValueType) { ValueType[KeyType] result; const ValueType[KeyType] res
 = result; }
 
 Usage: AA!(char[], int)
 
Simply assign the AA to null. That is the empty AA.
 -------------------------
 
 8) From the docs: >If the KeyType is a struct type, a default
 mechanism is used to compute the hash and comparisons of it based on
 the binary data within the struct value. A custom mechanism can be
 used by providing the following functions as struct members:< I think
 structs can have a default way to sort them too, lexicographically,
 that can be replaced by a custom mechanism when needed. How I have
 implemented it:
 
 int scmp(TyStruct1, TyStruct2)(TyStruct1 s1, TyStruct2 s2) { static
 if (s1.tupleof.length < s2.tupleof.length) { foreach(i, field;
 s1.tupleof) { if (field < s2.tupleof[i]) return -1; else if (field >
 s2.tupleof[i]) return 1; } return -1; } else { foreach(i, field;
 s2.tupleof) { if (field < s1.tupleof[i]) return 1; else if (field >
 s1.tupleof[i]) return -1; } static if (s1.tupleof.length ==
 s2.tupleof.length) return 0; else return 1; } }
 
 How I use it, to shorten the code I use to sort structs: struct P1 { 
 char[] k; int v; int opCmp(P1 other) { return scmp(this, other); } }
 
 I think dmd can do something like that by default.
 
Alternately, it could simply do a flat comparison of the bits behind the struct. If the structs have different members, I'm not sure how much sense it makes to do a lexicographical comparison of their members like that.
 -------------------------
 
 10) I think few useful properties/methods can be added to the
 built-in AAs: - aa.clear() to remove all key/val from the AA. -
 aa.dup, to create a copy of the AA. - aa.sort, that compulsively
 takes a 'key' function (that takes two arguments, key and value) and
 returns the sorted array of the keys. Here is a simple example of
 such sorting:
 
 TyKey[] sortedAA(TyKey, TyVal, TyFun)(TyVal[TyKey] aa, TyFun key) { 
 struct Pair { TyKey k; ReturnType!(TyFun) key_kv;
 
 int opCmp(Pair otherPair) { if (key_kv == otherPair.key_kv) return 0;
  return (key_kv < otherPair.key_kv) ? -1 : 1; } }
 
 Pair[] pairs; pairs.length = aa.length; uint i = 0; foreach(k, v; aa)
 { pairs[i] = Pair(k, key(k, v)); i++; }
 
 TyKey[] result; result.length = aa.length; foreach(ii, p; pairs.sort)
  result[ii] = p.k;
 
 return result; }
 
 You can use it with any key, like: TyV Vgetter(TyK, TyV)(TyK k, TyV
 v) { return v; } For the 10% of the lines of the program that need
 max speed or low memory usage, you can write sorting code the usual
 lower-level way.
 
An aa.dup would be nice. An a.clear() isn't necessarily needed; you can simply assign the AA reference to null, though of course this doesn't mutate the old AA.
 -------------------------
 
 12) From the docs:>Pointers in D can be broadly divided into two
 categories: those that point to garbage collected memory, and those
 that do not.< GC pointers don't support some operations, so why not
 define two kinds of pointers (gcpointer?), so the compiler can raise
 an error when the code tries to do one of the many illegal operations
 (= that produces undefined behavior) on GC pointers? (If it's needed
 a cast() may be used to convert a GC pointer to a normal pointer and
 vice versa).
 
Although it says there are two kinds of pointers, there is really only one kind of pointer. The only difference between these two kinds of pointers is the value: Some point to memory addresses in the range controlled by the GC, and some do not. I am not sure what qualifies as an illegal operation on one of these pointers that wouldn't on another. (Aside from deleting it, I suppose...)
 -------------------------
 
 15) On windows, expecially for newbies I think it can be positive to
 have a single zip with all necessary to use D (with dmd, dmc, bud and
 maybe dfl too).
 
I would say a dmd, dmc, and dsss package would be great.
 -------------------------
 
 17) It may be good if in dmd the -run parameter can be positioned
 anywhere in the command line.
 
As was mentioned in another branch of the thread, anything after the -run is considered as command-line arguments to the compiled program. -run terminates the option list to dmd.
 -------------------------
 
 18) In Python I find rather useful the built-in ** infix operator (it
 works among integers or floating point numbers):
 
 
 2 ** 3
8
 2 ** 0.5
1.4142135623730951
 -2 ** -2.5
-0.17677669529663689 std.math.pow is less useful when both arguments are integral.
I'd like to see this, too.
 -------------------------
 
 19) The dmd compiler can tell what variables aren't used (Delphi
 Pascal does this) or the ones that aren't modified after the
 initialization... (that is that are essentially unused).
 
It could. But Walter has said he doesn't like compiler warnings. But D is relatively easy to parse. A sufficiently advanced IDE could handle this sort of thing. -- Kirk McDonald http://kirkmcdonald.blogspot.com Pyd: Connecting D and Python http://pyd.dsource.org
Aug 01 2007
next sibling parent BCS <BCS pathlink.com> writes:
Kirk McDonald wrote:
 
 Personally, I almost always try to use selective imports in D:
 
 import std.stdio : writefln;
 
 But maybe that's my Python background. :-)
 
interesting you should mention that, I have a program that uses selective imports exclusively for all foreign code. I'm doing this because I'm expecting to port to Tango at some point and want to limit the phobos dependencies.
Aug 01 2007
prev sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Kirk McDonald wrote:
 3) I think it may be better to import "statically" by default (as in
 python, in that language people suggest to avoid the "from amodule
 import *" that means import all names from amodule).
Personally, I almost always try to use selective imports in D: import std.stdio : writefln; But maybe that's my Python background. :-) There was a massive thread on this newsgroup just before static, selective, and renaming imports were added. These were indeed inspired by Python's import semantics, but making everything a static import by default would have broken too much code, and was not deemed worth it. (However, imports were changed from public to private by default at that time.) A curious feature of D's imports is that with a regular "import foo;" you can still access the contents of foo by their fully-qualified name. This differs from Python's "from foo import *" in that, though all these imported names have been introduced into the namespace, you can still get at them if they have been shadowed by another name. Combined with the fact that someone importing your module will not get those imported names (since imports are private), the situation is just fine.
The situation is "just fine" from the code & compilers's perspective, that is. From the coder's perspective, however, the default mechanism has the same drawback as C++ #includes. The user has to be intimately familiar with the contents of every module included to analyze code someone else has written. Or you need to have a slick IDE that parses everything and figures out for you where various symbols are coming from. --bb
Aug 01 2007
next sibling parent downs <default_357-line yahoo.de> writes:
Bill Baxter wrote:
 The situation is "just fine" from the code & compilers's perspective, 
 that is.  From the coder's perspective, however, the default mechanism 
 has the same drawback as C++ #includes.  The user has to be intimately 
 familiar with the contents of every module included to analyze code 
 someone else has written.  Or you need to have a slick IDE that parses 
 everything and figures out for you where various symbols are coming from.
 
 --bb
I believe this is a social problem more than a technical one. Sometimes, you just need to develop a one-person program where you, obviously, know all imported modules - in this case I'd rather the language _not_ force you to use FQN. After all, when easily readable code is needed (say, in a corporate environment), the company in question can always simply make static (or selective) imports part of their style rules. There's nothing in D to _stop_ you from using FQN when required. So yes, the situation _is_ just fine :) --downs
Aug 02 2007
prev sibling parent reply Jascha Wetzel <"[firstname]" mainia.de> writes:
Bill Baxter wrote:
 Or you need to have a slick IDE that parses 
 everything and figures out for you where various symbols are coming from.
today's programming practice (and language design, as far as it's concerned) should be able to assume proper tools. many things like this can be tackled with semantics-aware features for editors. another example are complex naming conventions that emphasize type or scope of variables (like hungarian notation). what these things have in common is, that they make code less readable by including information that can be obtained in different ways. readability depends on the reader. the more familiar you are with the code, the less readable it becomes by additional information. the same holds true if you are only interested in the general function. i think, that the more real code looks like pseudo code, the better. i often start programming by writing down pseudo code in the editor. it's easier to mess around with and it isn't as officially separate like an upfront pen-and-paper design phase. after i'm happy with the pseudo code, i add the remaining detail. over and over i'm impressed about how little detail i need to add to make it valid D code. the closer it stays to the pseudo-code stage, the easier it is to maintain and refactor. therefore keeping it that way should be a good thing. if additional information can be gathered from an edit-time parser, it shouldn't be in the code. ...sorry for the rant ;)
Aug 02 2007
parent Nick Sabalausky <a a.a> writes:
Jascha Wetzel <[firstname] mainia.de> Wrote:

 Bill Baxter wrote:
 Or you need to have a slick IDE that parses 
 everything and figures out for you where various symbols are coming from.
today's programming practice (and language design, as far as it's concerned) should be able to assume proper tools. many things like this can be tackled with semantics-aware features for editors. another example are complex naming conventions that emphasize type or scope of variables (like hungarian notation). what these things have in common is, that they make code less readable by including information that can be obtained in different ways. readability depends on the reader. the more familiar you are with the code, the less readable it becomes by additional information. the same holds true if you are only interested in the general function. i think, that the more real code looks like pseudo code, the better. i often start programming by writing down pseudo code in the editor. it's easier to mess around with and it isn't as officially separate like an upfront pen-and-paper design phase. after i'm happy with the pseudo code, i add the remaining detail. over and over i'm impressed about how little detail i need to add to make it valid D code. the closer it stays to the pseudo-code stage, the easier it is to maintain and refactor. therefore keeping it that way should be a good thing. if additional information can be gathered from an edit-time parser, it shouldn't be in the code. ...sorry for the rant ;)
Sorry to veer so far offtopic, but you've just described one the main reasons I don't like Java's concept of checked exceptions (ie, all of that messy "throws" nonsense). (The other reason is it makes me feel like I've stepped right back into C's world of header file madness.)
Aug 02 2007
prev sibling next sibling parent reply bearophile <bearophileHUGS mailas.com> writes:
Sean Kelly>You can do this with Tango if you define a custom unit tester.<

I'll look at it.

-------------------------

Sean Kelly>What is the use for this over initializing to null?<

With that the creation of an AA becomes an expression, so you can use it inside
just a line, like

return AA!(int, string);

Instead of:

string[int] aa;
return aa;

That's a silly example, but expressions are useful in other situations too.
Note that for dynamic arrays you can do that already:

return new string[];

-------------------------

Sean Kelly>It's a good idea, but I actually like the fall-through behavior. I
use it regularly.<

I use it too :-)
But I think it causes bugs, now and then, and I've already seen that D cares
for common bugs, for example it forbids just the ; at the end of some loops,
you have to put an explicit {} that's less easy to miss.

Note (I am not suggesting this for D, I am just showing): Python cares even
more to avoid common bugs, you can't even do this:
while (a = b + c) { ... }
Assignments aren't expressions, this is forbidden just to avoid the bugs caused
to confusing == and =. (Pascal avoids part of those bugs because it uses := as
assignment, that's more easy to distinguish from = to test for equality).

-------------------------

Sean Kelly>I'm not sure if this solves the same problem, but Tango offers a
sort routine that takes a predicate.  It can be called as myArray.sort(&cmp) in
most cases, making it look much like a built-in feature.<

I have seen that Tango cmp, Python too used to have cmp only as auxiliary sort
argument, but in later Pythons they have introduced the key argument that's
much much nicer than the cmp of Tango (but it requires more RAM).

-------------------------

Sean Kelly>All sound useful, though it may be confusing to have AA.sort behave
differently than array sort.<

I agree. But they are different data structures, so maybe such "confusion" is
worth it.

-------------------------

Sean Kelly>I'm not sure I understand.  Could you explain this further?<

Okay, I'll try. Go in the D docs at the "Garbage Collector" page, it says:

Pointers in D can be broadly divided into two categories [...]
* Do not xor pointers with other values, like the xor pointer linked list trick used in C. * Do not use the xor trick to swap two pointer values. * Do not store pointers into non-pointer variables using casts and other tricks. * Do not take advantage of alignment of pointers to store bit flags in the low order bits: * Do not store into pointers values that may point into the garbage collected heap: * Do not store magic values into pointers, other than null. * Do not write pointer values out to disk and read them back in again. * Do not use pointer values to compute a hash function. A copying garbage collector can arbitrarily move objects around in memory, thus invalidating the computed hash value. * Do not depend on the ordering of pointers: * Do not add or subtract an offset to a pointer such that the result points outside of the bounds of the garbage collected object originally allocated. * Do not misalign pointers if those pointers may point into the gc heap, such as: * Do not use byte-by-byte memory copies to copy pointer values.< The list of diffences between GC pointers and "normal" (non-GC) pointers is so long and *complex*, that I belive the compiler can help the programmer to avoid all those complex pitfalls, creating two kinds (two types) of (mutually convertible with explicit casts) pointers, GC-pointers and nonGC-pointers. If you have two types the compiler can automatically enforce some (or all) those restrictions on the GC-pointers. ------------------------- BCS>shouldn't that be on line: dmd importer.d -run or am I misunderstanding?< Right. ------------------------- BCS>you should known what will be thrown and the function can do the assert, so I'd go with this< I am not sure, but it's possible your suggestion gives errors always on the same line, so it's not much useful during debugging. ------------------------- BCS>-run is at the end because all args after it are passed to the program, it's used a a delimiter< I undertands now. Then it can't be changed. ------------------------- Bruno Medeiros>Are you sure of that? It seems that at least for AA's, empty arrays are different than null arrays:< I see, I didn't know it. I avoid this bug because to test if an AA is empty I usually use if(aa.length){} (But I think the AA semantic you have shown needs some cleaning, othewise newbies may be bitten by it :-) ). ------------------------- Kirk McDonald>Greetings!< Thank you, you are quite friendly. -------------------------
(I rewrapped your post's linebreaks, but it seems this destroyed your code
formatting. Oops.)<
I see. It doesn't matter much, others can look at my original post. ------------------------- Kirk McDonald>I also suggest looking at DSSS: http://www.dsource.org/projects/dsss/ The rebuild utility is part of dsss. Rebuild is very much like bud, but is more actively maintained. I also prefer its treatment of object files to bud's.< I'll take a look at it. The only thing I don't like of "bud" is that if you use -exec it doesn't delete the executable from the disk after the running (as dmd too does if you use -run). ------------------------- Kirk McDonald>The issue of bundling bud-like functionality into dmd has been brought up before, but I don't think Walter is all that keen on the idea.< I don't see the advantage of having the programmer to state twice what modules to import. (But bud essentially solves the problem anyway, so it's not a big problem anymore for me). ------------------------- Kirk McDonald>Personally, I almost always try to use selective imports in D:< import std.stdio : writefln;< Me too. ------------------------- Kirk McDonald>But maybe that's my Python background. :-)< I too use Python a lot (You can see I have "thousands" of posts on Python newsgroups), and I use other languages as well. And I have already seen D contains tons of ideas directly copied/adapted from Python (and 95% of the times that's positive because the main quality of Python is that it is user friendly, the programmer is more important than the other things). ------------------------- Kirk McDonald>There was a massive thread on this newsgroup just before static, selective, and renaming imports were added. These were indeed inspired by Python's import semantics, but making everything a static import by default would have broken too much code, and was not deemed worth it.< I understand. D 2.0+ isn't compatible with D 1.0+, so now with D2.0 it's a good time to fix all such problems (Python too is going toward the 3.0 version whose main purpose is to clean up things, breaking compatibility with older versions). ------------------------- Kirk McDonald>(However, imports were changed from public to private by default at that time.)< That's very important. My general advice: copying one Python feature is often fine if you copy a certain Python feature all the way, if you stop and you copy only half of that feature than you may end in troubles :-) Regarding modules I belive D has copied only part of Python, and this causes some (little) problems. ------------------------- Kirk McDonald>Simply assign the AA to null. That is the empty AA.< It seems you all have missed my point here (probably for my fault), I was talking about creating (and defining) an AA, not cleaning an existing one. See my answer to Sean Kelly. ------------------------- Kirk McDonald>If the structs have different members,< I was mostly talking about two structs with the same member types. But even when members aren't exactly the same, some times they can implicitely casted to the other (example: byte <==> int). ------------------------- Kirk McDonald>Although it says there are two kinds of pointers, there is really only one kind of pointer.< My suggestion it to create two types, so they become really two different kinds of poiters for the compiler. ------------------------- Kirk McDonald>I am not sure what qualifies as an illegal operation on one of these pointers that wouldn't on another. (Aside from deleting it, I suppose...)< The D docs list all those differences (you can see a summary in my answer for Sean Kelly too). ------------------------- Kirk McDonald>I would say a dmd, dmc, and dsss package would be great.< Some other person can do this packaging, I think it's not necessary for Walter himself to do that. I presume he is already busy enough. With time he will learn to delegate more more work to others. ------------------------- Kirk McDonald>It could. But Walter has said he doesn't like compiler warnings. But D is relatively easy to parse. A sufficiently advanced IDE could handle this sort of thing.< Beside Delphi, I think C# compiler too shows such "suggestions" to the programmer, and the C# programmers I know seem rather happy to receive such things, so I belive D can have them too. They are quite handy and I don't see anything they can harm if they are well chosen and they are few. ------------------------- Bill Baxter>Or you need to have a slick IDE that parses everything and figures out for you where various symbols are coming from.< In real D code I always use only selective imports, and I add a comment that shows what's the usage of those imported things: import std.string: join; // for bar() function import std.math: abs; // for foo() function ... In Python the from modulename import * (that imports all names from a module into the current namespace) is considered bad practice, and acceptable only during interactive usage from the shell (that I miss in D, but I don't think it's easy to create a *full* D interpreter) or for tiny single-usage progams. ------------------------- Thank you for the many answers, you are quite friendly! After the weekend I'll try to write a second post of things/suggestions (but I have already shown you most of the important ones). Bear hugs, bearophile
Aug 03 2007
parent reply "Vladimir Panteleev" <thecybershadow gmail.com> writes:
On Fri, 03 Aug 2007 10:39:41 +0300, bearophile <bearophileHUGS mailas.co=
m> wrote:

 With that the creation of an AA becomes an expression, so you can use =
it inside just a line, like
 return AA!(int, string);

 Instead of:

 string[int] aa;
 return aa;

 That's a silly example, but expressions are useful in other situations=
too.
 Note that for dynamic arrays you can do that already:

 return new string[];
In both cases you can just "return null;", which will work in most cases= .
 Note (I am not suggesting this for D, I am just showing): Python cares=
even more to avoid common bugs, you can't even do this:
 while (a =3D b + c) { ... }
 Assignments aren't expressions, this is forbidden just to avoid the bu=
gs caused to confusing =3D=3D and =3D. (Pascal avoids part of those bugs= because it uses :=3D as assignment, that's more easy to distinguish fro= m =3D to test for equality). You can't do this in D either ('=3D' does not give a boolean result). Ch= eck your facts before posting ;)
 Sean Kelly>All sound useful, though it may be confusing to have AA.sor=
t behave differently than array sort.<
 I agree. But they are different data structures, so maybe such "confus=
ion" is worth it. Have you considered using: foreach(key;aa.keys.sort) writefln(key, " =3D> ", aa[key]);
 Kirk McDonald>Simply assign the AA to null. That is the empty AA.<

 It seems you all have missed my point here (probably for my fault), I =
was talking about creating (and defining) an AA, not cleaning an existin= g one. See my answer to Sean Kelly. "null" works as a "literal" expression for arrays and AAs. That's why "c= leaning" works - it simply sets the reference to null, and the GC will c= lean up the old contents sooner or later. P.S. Your method of replying is very confusing, it's almost impossible t= o find the actual context of your messages. What newsreader software do = you use? -- = Best regards, Vladimir mailto:thecybershadow gmail.com
Aug 03 2007
parent bearophile <bearophileHUGS lycos.com> writes:
Vladimir Panteleev>In both cases you can just "return null;", which will work in
most cases.<

My stuff is just made of templates, they work finding the types of inputs, and
null doesn't have the type of keys and values.


You can't do this in D either ('=' does not give a boolean result). Check your
facts before posting ;)< You are right, and I'm a newbie still of D. To make that work you have to use an explicit cast(bool), so that bug is avoided. Vladimir Panteleev>Have you considered using: foreach(key;aa.keys.sort)< I was talking about ways to solve more complex situations, that is a key() function that takes both key and value before creating an item that's the one to be sorted according to, that's useful when you have to sort according to values or according to both keys and values, etc. Vladimir Panteleev>What newsreader software do you use?< I don't use it. Bye, bearophile (remove HUGS to email me)
Aug 07 2007
prev sibling next sibling parent reply Lionello Lunesu <lio lunesu.remove.com> writes:
 7) From the FAQ: 
 Many people have asked for a requirement that there be a break
 between cases in a switch statement, that C's behavior of
 silently falling through is the cause of many bugs.
 The reason D doesn't change this is for the same reason that
 integral promotion rules and operator precedence rules were
 kept the same - to make code that looks the same as in C
 operate the same. If it had subtly different semantics, it
 will cause frustratingly subtle bugs.
 I agree with both points of view. My idea: calling this
 statement differently (like caseof) instead of "switch"
 (like in Pascal), so you can change its semantics too,
 removing the falling through (you may use the Pascal
 semantic too).
Sorry to hijack your point here, but this got me thinking: Why not use "continue" for seeping through to the next case statement? DMD could then complain if a case does not end with break/continue/goto/return and silently insert a assert(0) before each case (the way it does for functions that return a value.) L.
Aug 03 2007
parent reply Don Clugston <dac nospam.com.au> writes:
Lionello Lunesu wrote:
 7) From the FAQ: 
 Many people have asked for a requirement that there be a break
> between cases in a switch statement, that C's behavior of > silently falling through is the cause of many bugs. > The reason D doesn't change this is for the same reason that > integral promotion rules and operator precedence rules were > kept the same - to make code that looks the same as in C > operate the same. If it had subtly different semantics, it > will cause frustratingly subtle bugs.
 I agree with both points of view. My idea: calling this
> statement differently (like caseof) instead of "switch" > (like in Pascal), so you can change its semantics too, > removing the falling through (you may use the Pascal > semantic too). Sorry to hijack your point here, but this got me thinking: Why not use "continue" for seeping through to the next case statement? DMD could then complain if a case does not end with break/continue/goto/return and silently insert a assert(0) before each case (the way it does for functions that return a value.)
Doesn't for (c;;) { switch (c) { case 'a': continue; case 'b': break; } } already have a meaning?
Aug 03 2007
next sibling parent Robert Fraser <fraserofthenight gmail.com> writes:
 Doesn't
 
 for (c;;) {
    switch (c) {
    case 'a': continue;
    case 'b': break;
    }
 }
 
 already have a meaning?
That always bothered me: break and continue both have similar meanings in every place they're used (in loops), except if there's a switch statement, where suddenly break no longer works to break the loop but continue does. I'd very much like to see continue mean fall-through, but if that's not an option, making an unlabeled continue in a switch an error would help increase orthagonality.
Aug 03 2007
prev sibling next sibling parent reply Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
Don Clugston wrote:
 Lionello Lunesu wrote:
 7) From the FAQ: 
 Many people have asked for a requirement that there be a break
> between cases in a switch statement, that C's behavior of > silently falling through is the cause of many bugs. > The reason D doesn't change this is for the same reason that > integral promotion rules and operator precedence rules were > kept the same - to make code that looks the same as in C > operate the same. If it had subtly different semantics, it > will cause frustratingly subtle bugs.
 I agree with both points of view. My idea: calling this
> statement differently (like caseof) instead of "switch" > (like in Pascal), so you can change its semantics too, > removing the falling through (you may use the Pascal > semantic too). Sorry to hijack your point here, but this got me thinking: Why not use "continue" for seeping through to the next case statement? DMD could then complain if a case does not end with break/continue/goto/return and silently insert a assert(0) before each case (the way it does for functions that return a value.)
Doesn't for (c;;) { switch (c) { case 'a': continue; case 'b': break; } } already have a meaning?
I can't think of any other case where this problem would show (aside from other looping statements, so the fix is the same). Since this is probably not an overly common case (I've done it myself, but not very often), I don't think writing a label on the loop would be a big deal. I could live with fall-thru-by-continue. Maybe if looping statements had a shorthand form for labeling, it could be even cleaner... for loop (c;;) { switch (c) { case 'a': continue loop; case 'b': break loop; } } -- Chris Nicholson-Sauls
Aug 05 2007
parent reply Don Clugston <dac nospam.com.au> writes:
Chris Nicholson-Sauls wrote:
 Don Clugston wrote:
 Lionello Lunesu wrote:
 7) From the FAQ: 
 Many people have asked for a requirement that there be a break
> between cases in a switch statement, that C's behavior of > silently falling through is the cause of many bugs. > The reason D doesn't change this is for the same reason that > integral promotion rules and operator precedence rules were > kept the same - to make code that looks the same as in C > operate the same. If it had subtly different semantics, it > will cause frustratingly subtle bugs.
 I agree with both points of view. My idea: calling this
> statement differently (like caseof) instead of "switch" > (like in Pascal), so you can change its semantics too, > removing the falling through (you may use the Pascal > semantic too). Sorry to hijack your point here, but this got me thinking: Why not use "continue" for seeping through to the next case statement? DMD could then complain if a case does not end with break/continue/goto/return and silently insert a assert(0) before each case (the way it does for functions that return a value.)
Doesn't for (c;;) { switch (c) { case 'a': continue; case 'b': break; } } already have a meaning?
I can't think of any other case where this problem would show (aside from other looping statements, so the fix is the same). Since this is probably not an overly common case (I've done it myself, but not very often), I don't think writing a label on the loop would be a big deal.
That's fine. The issue is that it would silently break existing code.
Aug 06 2007
parent Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
Don Clugston wrote:
 Chris Nicholson-Sauls wrote:
 Don Clugston wrote:
 Lionello Lunesu wrote:
 7) From the FAQ: 
 Many people have asked for a requirement that there be a break
> between cases in a switch statement, that C's behavior of > silently falling through is the cause of many bugs. > The reason D doesn't change this is for the same reason that > integral promotion rules and operator precedence rules were > kept the same - to make code that looks the same as in C > operate the same. If it had subtly different semantics, it > will cause frustratingly subtle bugs.
 I agree with both points of view. My idea: calling this
> statement differently (like caseof) instead of "switch" > (like in Pascal), so you can change its semantics too, > removing the falling through (you may use the Pascal > semantic too). Sorry to hijack your point here, but this got me thinking: Why not use "continue" for seeping through to the next case statement? DMD could then complain if a case does not end with break/continue/goto/return and silently insert a assert(0) before each case (the way it does for functions that return a value.)
Doesn't for (c;;) { switch (c) { case 'a': continue; case 'b': break; } } already have a meaning?
I can't think of any other case where this problem would show (aside from other looping statements, so the fix is the same). Since this is probably not an overly common case (I've done it myself, but not very often), I don't think writing a label on the loop would be a big deal.
That's fine. The issue is that it would silently break existing code.
Okay, I'll grant you that. Maybe require the 'switch' keyword after the continue? Or else require a label on the switch... but then continues start acting slightly different from breaks. No easy way out, it seems. -- Chris Nicholson-Sauls
Aug 06 2007
prev sibling parent reply Lionello Lunesu <lio lunesu.remove.com> writes:
Don Clugston wrote:
 Lionello Lunesu wrote:
 7) From the FAQ: 
 Many people have asked for a requirement that there be a break
> between cases in a switch statement, that C's behavior of > silently falling through is the cause of many bugs. > The reason D doesn't change this is for the same reason that > integral promotion rules and operator precedence rules were > kept the same - to make code that looks the same as in C > operate the same. If it had subtly different semantics, it > will cause frustratingly subtle bugs.
 I agree with both points of view. My idea: calling this
> statement differently (like caseof) instead of "switch" > (like in Pascal), so you can change its semantics too, > removing the falling through (you may use the Pascal > semantic too). Sorry to hijack your point here, but this got me thinking: Why not use "continue" for seeping through to the next case statement? DMD could then complain if a case does not end with break/continue/goto/return and silently insert a assert(0) before each case (the way it does for functions that return a value.)
Doesn't for (c;;) { switch (c) { case 'a': continue; case 'b': break; } } already have a meaning?
Yes, I've done that too, but honestly I'd be the first one to admit that it isn't very clear. In fact, I'd go so far as to call it a reason to attach a meaning to "continue" in a switch/case. As mentioned, it would break existing code :( L.
Aug 08 2007
parent Don Clugston <dac nospam.com.au> writes:
Lionello Lunesu wrote:
 Don Clugston wrote:
 Lionello Lunesu wrote:
 7) From the FAQ: 
 Many people have asked for a requirement that there be a break
> between cases in a switch statement, that C's behavior of > silently falling through is the cause of many bugs. > The reason D doesn't change this is for the same reason that > integral promotion rules and operator precedence rules were > kept the same - to make code that looks the same as in C > operate the same. If it had subtly different semantics, it > will cause frustratingly subtle bugs.
 I agree with both points of view. My idea: calling this
> statement differently (like caseof) instead of "switch" > (like in Pascal), so you can change its semantics too, > removing the falling through (you may use the Pascal > semantic too). Sorry to hijack your point here, but this got me thinking: Why not use "continue" for seeping through to the next case statement? DMD could then complain if a case does not end with break/continue/goto/return and silently insert a assert(0) before each case (the way it does for functions that return a value.)
Doesn't for (c;;) { switch (c) { case 'a': continue; case 'b': break; } } already have a meaning?
Yes, I've done that too, but honestly I'd be the first one to admit that it isn't very clear. In fact, I'd go so far as to call it a reason to attach a meaning to "continue" in a switch/case.
Including making it illegal. Since there's no short syntax to break out of out an outer loop from inside a switch, I don't see why a bare 'continue' is legal. (actually the spec doesn't mention it, so it's a bit ambiguous). Especially since "continue <identifier>;" and "break <identifier>;" exist, and are clearer.
 As mentioned, it would break existing code :(
Searching for "continue; case" on google gave many hits for C-family languages. So even if it didn't break any existing D code, it would still be a problem for porting from other languages. I think that's a show-stopper. Which is a shame, because I do like the idea.
Aug 09 2007
prev sibling parent reply BCS <BCS pathlink.com> writes:
bearophile wrote:
 7) From the FAQ: >Many people have asked for a requirement that there be a
break between cases in a switch statement, that C's behavior of silently
falling through is the cause of many bugs. The reason D doesn't change this is
for the same reason that integral promotion rules and operator precedence rules
were kept the same - to make code that looks the same as in C operate the same.
If it had subtly different semantics, it will cause frustratingly subtle bugs.<
 
 I agree with both points of view. My idea: calling this statement differently
(like caseof) instead of "switch" (like in Pascal), so you can change its
semantics too, removing the falling through (you may use the Pascal semantic
too).
 
how about keep switch as is and add "select" with the no fall through. I wouldn't mind having nothing done about this, but don't change the switch!
Aug 08 2007
parent reply Paul Findlay <r.lph50+d gmail.com> writes:
BCS wrote:
 how about keep switch as is and add "select" with the no fall through.
 
 I wouldn't mind having nothing done about this, but don't change the
 switch!
Does just using goto case; or goto case Location; make a good substitute? At least it makes the fall through in a switch statement quite explicit. - Paul
Aug 08 2007
parent BCS <ao pathlink.com> writes:
Reply to Paul,

 BCS wrote:
 
 how about keep switch as is and add "select" with the no fall
 through.
 
 I wouldn't mind having nothing done about this, but don't change the
 switch!
 
Does just using goto case; or goto case Location; make a good substitute? At least it makes the fall through in a switch statement quite explicit. - Paul
Believe it or not, I may actually want fall through more often then not, and in some of those cases the goto case #; option is not at all pleasant.
Aug 08 2007