digitalmars.D - My thoughts & experiences with D so far, as a novice D coder
- Vidar Wahlberg (78/78) Mar 27 2013 I know I'm probably going to upset some people with this, bashing
- John Colvin (3/11) Mar 27 2013 int i = 5;
- bearophile (27/49) Mar 27 2013 Beside using returnType as suggested, another solution is to add
- H. S. Teoh (10/17) Mar 27 2013 [...]
- renoX (7/13) Mar 28 2013 Especially when keeping the poor C/C++ syntax for declaration
- Vidar Wahlberg (51/51) Mar 28 2013 To follow up with some new woes I'm currently struggling with:
- H. S. Teoh (43/48) Mar 28 2013 [...]
- 1100110 (6/52) Mar 28 2013 Yes, some functions do overload one another's functions...
- Chris Cain (15/21) Mar 28 2013 I am completely confused as to why you're doing what you are
- Vidar Wahlberg (10/20) Mar 28 2013 This is not what I'm trying to achieve.
- Chris Cain (31/34) Mar 28 2013 Ah, I reread it a couple of times and realized what you mean now.
- Chris Cain (35/52) Mar 28 2013 Sorry about the repeated postings ... I'm trying to read & answer
- Chris Cain (8/15) Mar 28 2013 This should be:
- Vidar Wahlberg (18/25) Mar 28 2013 Well, I'm not so proficient in the language yet that I'm going to
- Jonathan M Davis (20/23) Mar 28 2013 Because read and write operate in place, and append doesn't. read and wr...
- Vidar Wahlberg (30/34) Mar 28 2013 Thank you, I will read that (when the time is not 0400).
- Jonathan M Davis (9/19) Mar 28 2013 I don't think that it's unthinkable at all (and arrays _are_ ranges; it'...
- Jesse Phillips (46/46) Mar 28 2013 Definitely need to add some updates to the docs. Long story:
- Jonathan M Davis (11/19) Mar 28 2013 That's because it's operating on multiple bytes at a time (e.g. writing ...
- Jesse Phillips (6/13) Mar 29 2013 Ah, I was just going by the docs when I wrote this. I apparently
- Jonathan M Davis (6/9) Mar 29 2013 I'll have to take a look at that. I think that it was pretty much assumi...
- deadalnix (47/126) Mar 27 2013 That is fine don't worry. Knowing what people have trouble with
- Timon Gehr (5/37) Mar 27 2013 I strongly disagree. What would be an example of the problems you are
- Brad Anderson (3/15) Mar 27 2013 I believe that's what this pull aims to do:
- Timon Gehr (2/15) Mar 27 2013 Almost. It does not support TLS, which is a severe limitation.
- deadalnix (21/23) Mar 27 2013 T foo(alias fallback)() {
- Nick Sabalausky (53/82) Mar 27 2013 A "does not return" return type would be nice for other things anyway.
- Timon Gehr (4/28) Mar 27 2013 I see. What is needed is a way to specify that a function does never
- Vidar Wahlberg (19/41) Mar 27 2013 Won't this create an array (with 5 elements) of arrays (with 5
- H. S. Teoh (14/37) Mar 27 2013 Yeah, the documentation needs to be improved. Maybe file an enhancement
- Nick Sabalausky (22/42) Mar 27 2013 Yea, the arrays definitely do blur the value/reference lines. Aside
- John Colvin (4/12) Mar 27 2013 Unfortunately yes.
- H. S. Teoh (8/25) Mar 27 2013 Which is what Denis' multidimensional array implementation does. As does
- Andrei Alexandrescu (3/7) Mar 27 2013 Agree. Do you (or Denis) have something in reviewable form?
- H. S. Teoh (11/19) Mar 27 2013 Here is Denis' implementation:
- Denis Shelomovskij (11/19) Oct 08 2013 No. The module is 2 years old and I only made changes on dmd requests.
- Andrei Alexandrescu (5/9) Mar 27 2013 Ideally such function should return a "none" type, the bottom of the
- Timon Gehr (5/15) Mar 27 2013 Maybe it is one next best choice. It is still a horrible choice.
- deadalnix (6/18) Mar 27 2013 I thought that typeof(null) was that bottom type. What is the
- Timon Gehr (7/22) Mar 28 2013 There is a huge difference.
- deadalnix (3/37) Mar 28 2013 I don't see how it is worse.
- Dicebot (18/62) Mar 27 2013 That is somewhat intended as D proposes message-passing a
- John Colvin (14/30) Mar 27 2013 This is actually nothing to do with auto. It's endemic to all
- John Colvin (3/6) Mar 27 2013 Sorry, I'd want to correct this to "This is only partly to do
- Nick Sabalausky (82/131) Mar 27 2013 I haven't been dealing with threads or "shared", but my
- H. S. Teoh (23/74) Mar 27 2013 What's wrong with using typeof?
- deadalnix (4/21) Mar 27 2013 It can. myOptions is an int here, as Options would decay to its
- Steven Schveighoffer (4/25) Mar 27 2013 No, it's not. try it. I thought as you did too until recently.
- deadalnix (5/34) Mar 27 2013 I knew the bug existed, but I thought it would be solved now. If
- Nick Sabalausky (3/29) Mar 27 2013 Options myOptions = Options.FeatureA | Options.FeatureC;
- bearophile (7/26) Mar 27 2013 This was discussed in past. A library code BitFlags similar to
- H. S. Teoh (63/109) Mar 27 2013 This is true. Sometimes you really *do* want to know what the return
- Nick Sabalausky (8/20) Mar 27 2013 I wouldn't want such things to be implicitly converted to static ctors
- H. S. Teoh (14/37) Mar 27 2013 I wonder if it would solve the problem if different instances of static
- Steven Schveighoffer (19/23) Mar 27 2013 Typically, one uses std.conv.to to safely convert one value into another...
- Nick Sabalausky (4/21) Mar 27 2013 Works for me on 2.063 Win. Keep in mind:
- Nick Sabalausky (3/27) Mar 27 2013 I meant of course 2.062
- Steven Schveighoffer (6/33) Mar 27 2013 Hah, I have not yet downloaded 2.062. It did not work in 2.061, not sur...
- 1100110 (3/39) Mar 28 2013 It worked for me in 2.061, as that's what I'm currently on.
- 1100110 (16/39) Mar 28 2013 It also works on values....
- Jesse Phillips (33/66) Mar 27 2013 I think D has quite a strong set of critic users, namely because
- Dmitry Olshansky (24/82) Mar 27 2013 Currently documentation for match states in plain text that:
- Andrei Alexandrescu (24/28) Mar 27 2013 [snip]
- H. S. Teoh (31/36) Mar 27 2013 [...]
- Jacob Carlborg (6/14) Mar 28 2013 Try message passing and serialize the data you want to send.
I know I'm probably going to upset some people with this, bashing their favourite child and all, but I wanted to let you know the experience I've had with D so far, as a novice D coder with a heavy Java & light C++ background. It's not that I dislike D, in fact there are tons of things I love about it, it's pretty much exactly what I'm looking for in a programming language at the moment. Yet, I encounter some frustrating issues when coding, often leaving me with the impression that I'm fighting the language more than the problem I'm trying to solve. True, there are many things I don't know about D, compilers or the inner workings of a computer, and some of the fights I have with the language are likely started by myself because I'm dragging along my bias from other languages, drawing misconceptions on how the D language actually works. My intentions are not to insult, but shed some light on some of the difficulties I've faced (and how I solved them), with the hope that it will help others from facing the same difficulties. Woes: ----- - I find myself in a world of pain when I want to share data more complex than the basic data types (int, char, byte, etc) across threads. Seemingly the magic trick is to "cast(shared) foo" (or "cast(immutable)") when passing objects/references to another thread, then "cast(Foo)" back on the receiving end (as most classes/structs in the standard library refuse to let you call any methods when the object is shared). The examples in the source and TDPL are fairly limited on the issue, it mostly covers only those basic data types. - While the "auto"-keyword often is great, it can lead to difficulties, especially when used as the return type of a function, such as "auto foo() { return bar; }". Sometimes you may wish to store the result of a function/method call as a global variable/class member, but when the function/method returns "auto" it's not apparent what the data type may be. While you may be able to find out what "bar" is by digging in the source code, it can still be difficult to find. One example is to save the result of "std.regex.match()" as a member in a class. For me the solution was to "import std.traits", create a function "auto matchText(string text) { return match(text, myRegex); }" and define the class member as "ReturnType!matchText matchResult;" (do also note that function & member must come in the right order for this to compile). This was all but obvious to a novice D coder as myself, the solution was suggested to me in the IRC channel. Gotchas: -------- - The lack of "rectangular" arrays created at runtime in D ("int i = 5; int[i][i] foo;") can be quite confusing for programmers with Java or C++ background. Even though there exists alternatives (http://denis-sh.github.com/phobos-additions/unstd.multidimensionalarray.html), this design decision and how to get around it when you really desire a "rectangular" array could be explained in more detail at http://dlang.org/arrays.html. - Static array versus dynamic array was one of the first traps I stepped on (http://forum.dlang.org/thread/jnu1an$rjr$1 digitalmars.com). Until Jonathan M. Davis explained it in detail (http://d.puremagic.com/issues/show_bug.cgi?id=8026#c4), I pretty much considered it as "magic" that "randomShuffle(staticArray);" did not sort the array while "randomShuffle(staticArray[]);" did (the first call now gives you an compile error, though). That static arrays are value types while dynamic arrays are reference types may not be obvious for those with primarily Java background. - When casting a value to an enum, there's no checking that the value actually is a valid enum value. Don't think I ever found a solution on how to check whether the value after casting is a valid enum value, it hasn't been a pressing issue. - Compiling where DMD can't find all modules cause a rather cryptic error message. A solution is to make sure you specify all source files when compiling. Wishlist: --------- - "void[T]" associative array (i.e. a "set") would be nice, can be achieved with "byte[0][T]". - "Foo foo = new Foo();" for global variables/class members. Now you must "Foo foo; static this() { foo = new Foo(); }".
Mar 27 2013
On Wednesday, 27 March 2013 at 15:34:20 UTC, Vidar Wahlberg wrote:- The lack of "rectangular" arrays created at runtime in D ("int i = 5; int[i][i] foo;") can be quite confusing for programmers with Java or C++ background. Even though there exists alternatives (http://denis-sh.github.com/phobos-additions/unstd.multidim nsionalarray.html), this design decision and how to get around it when you really desire a "rectangular" array could be explained in more detail at http://dlang.org/arrays.html.int i = 5; auto foo = new int[][](i,i);
Mar 27 2013
Vidar Wahlberg:- While the "auto"-keyword often is great, it can lead to difficulties, especially when used as the return type of a function, such as "auto foo() { return bar; }". Sometimes you may wish to store the result of a function/method call as a global variable/class member, but when the function/method returns "auto" it's not apparent what the data type may be. While you may be able to find out what "bar" is by digging in the source code, it can still be difficult to find.Beside using returnType as suggested, another solution is to add in your code something like this: pragma(msg, typeof(foo)); This tells you the type to use for your class member. Haskell solves this in a better way, using "Type holes": http://www.haskell.org/haskellwiki/GHC/TypeHoles The idea of those holes was developed in several complex ways, but at its smallest it is just a way offered by the compiler to the Haskell programmer to leave one thing explicitly not specified. The program will not compile, but the error message will tell you very well the type of what's missing. So you use this type information to put in the hole what the compiler wants.That static arrays are value types while dynamic arrays are reference types may not be obvious for those with primarily Java background.Java has a semantics more limited compared to a system language as D/Rust. This is not easy to avoid. On the other hand iterating on an array of structs/fixed size arrays has a trap that a D lint should warn against.- When casting a value to an enum, there's no checking that the value actually is a valid enum value. Don't think I ever found a solution on how to check whether the value after casting is a valid enum value, it hasn't been a pressing issue.cast() is a sharp unsafe tool. In Bugzilla I have a request to use to!() to perform that safely.- Compiling where DMD can't find all modules cause a rather cryptic error message.This was improved, and maybe there is further space for improvements. It's a fixable problem.Wishlist: --------- - "void[T]" associative array (i.e. a "set") would be nice, can be achieved with "byte[0][T]".I think there's no need to add that as a built-in. There are things much more important to have as builtins (like tuples). For that a Phobos Set!T suffices.- "Foo foo = new Foo();" for global variables/class members.Maybe in some time it will happen, thanks to Don too. Bye, bearophile
Mar 27 2013
On Wed, Mar 27, 2013 at 05:04:48PM +0100, bearophile wrote: [...][...] It already does that on latest git phobos: enum A { abc=100, def=200 } A a; a = to!A(150); // throws runtime exception T -- All problems are easy in retrospect.- When casting a value to an enum, there's no checking that the value actually is a valid enum value. Don't think I ever found a solution on how to check whether the value after casting is a valid enum value, it hasn't been a pressing issue.cast() is a sharp unsafe tool. In Bugzilla I have a request to use to!() to perform that safely.
Mar 27 2013
On Wednesday, 27 March 2013 at 16:04:49 UTC, bearophile wrote:Vidar Wahlberg:[cut]Especially when keeping the poor C/C++ syntax for declaration instead of Pascal-style declaration syntax where you *name* the kind of array you're using! *Sigh* and some still think that syntax doesn't matter.. renoXThat static arrays are value types while dynamic arrays are reference types may not be obvious for those with primarily Java background.Java has a semantics more limited compared to a system language as D/Rust. This is not easy to avoid.
Mar 28 2013
To follow up with some new woes I'm currently struggling with: I'm storing some various values in an ubyte array. I discovered that it's probably std.bitmanip I wish to use in order to "convert" i.e. an int to 4 bytes (although I went first to std.conv looking for this feature). So I have "ubyte[] buffer;", and my second thought is that the "append" method want to append values to my ubyte-array (my first thought was something like "buffer ~= to!ubyte[](42);", although then I forgot about endianness). In the example in the documentation it does say "auto buffer = appender!(const ubyte[])();", with no explanation as of what "appender" is (I later learned that this is from std.array), but just looking a bit up I see that the "write" method explained just above use "ubyte[] buffer; buffer.write!ubyte(42);", so I assumed that I could use ubyte[] myself instead of this "appender" which I thought was some legacy code. So I write some simple test code: import std.bitmanip, std.stdio; void main() { ubyte[] buffer; buffer.append!ubyte(42); } Run it through rdmd, and get: "core.exception.AssertError /usr/include/d/std/array.d(591): Attempting to fetch the front of an empty array of ubyte". Just to see what happens I set the size of the buffer ("buffer.length = 1;") before appending and run it again. Now it runs, but instead of appending it behaves like write(), which was not exactly what I wanted. At this time I google for this "appender" used in the example and learn that it comes from std.array, so I import std.array and try again using "auto buffer = appender!(ubyte[])();", and surely enough, now it does append correctly to the buffer. Great, I have a solution, so I go back to my project and implement it like I implemented it in my test code, but when I compile my project after this addition I get a new cryptic error message: "Error: __overloadset isn't a template". After digging a bit I realized that it's because in my project I also import std.file, apparently there are some collisions between std.bitmanip and std.file. Again it's solvable, but it's yet another fight with the language/standard library. I would also assume that it's not that uncommon for a module that use std.bitmanip to also use std.file, meaning that this error potentially may occur often. A bit on the side: It seems to me as importing std.bitmanip somehow adds new properties to my array (".read()" and ".write()", for example). Not necessarily a bad thing, more of "I've not seen this before, I was expecting that I were to concatenate the bytes from the conversion to my buffer using ~".
Mar 28 2013
On Thu, Mar 28, 2013 at 09:24:49PM +0100, Vidar Wahlberg wrote:To follow up with some new woes I'm currently struggling with: I'm storing some various values in an ubyte array. I discovered that it's probably std.bitmanip I wish to use in order to "convert" i.e. an int to 4 bytes (although I went first to std.conv looking for this feature).[...] There are several ways to convert an int into 4 bytes: 1) Use a union: static assert(int.sizeof==4); ubyte[4] intToUbytes(int x) { union U { int i; ubyte[4] b; } U u; u.i = x; return u.b; } 2) Use bit operators: ubyte[4] intToUbytes(int x) { ubyte[4] bytes; // Note: this assumes little-endian. For big-endian, // reverse the order below. bytes[0] = x & 0xFF; bytes[1] = (x >> 8) & 0xFF; bytes[2] = (x >> 16) & 0xFF; bytes[3] = (x >> 24) & 0xFF; return bytes; } 3) Use a pointer cast (warning: un- safe): ubyte[4] intToUbytes(int x) system { ubyte[4] b; ubyte* ptr = cast(ubyte*)&x; b[0] = *ptr++; b[1] = *ptr++; b[2] = *ptr++; b[3] = *ptr; return b; } 4) Reinterpret a pointer (warning: un- safe): ubyte[4] intToUbytes(int x) system { return *cast(ubyte[4]*)&x; } I'm sure there are several other ways to do it. You don't need to use appender unless you're doing a lot of conversions in one go. --T
Mar 28 2013
On 03/28/2013 03:24 PM, Vidar Wahlberg wrote:To follow up with some new woes I'm currently struggling with: I'm storing some various values in an ubyte array. I discovered that it's probably std.bitmanip I wish to use in order to "convert" i.e. an int to 4 bytes (although I went first to std.conv looking for this feature). So I have "ubyte[] buffer;", and my second thought is that the "append" want to append values to my ubyte-array (my first thought was something like "buffer ~= to!ubyte[](42);", although then I forgot about endianness). In the example in the documentation it does say "auto buffer = appender!(const ubyte[])();", with no explanation as of what "appender" is (I later learned that this is from std.array), but just looking a bit up I see that the "write" method explained just above use "ubyte[] buffer; buffer.write!ubyte(42);", so I assumed that I could use ubyte[] myself instead of this "appender" which I thought was some legacy code. So I write some simple test code: import std.bitmanip, std.stdio; void main() { ubyte[] buffer; buffer.append!ubyte(42); } Run it through rdmd, and get: "core.exception.AssertError /usr/include/d/std/array.d(591): Attempting to fetch the front of an empty array of ubyte". Just to see what happens I set the size of the buffer ("buffer.length = 1;") before appending and run it again. Now it runs, but instead of appending it behaves like write(), which was not exactly what I wanted. At this time I google for this "appender" used in the example and learn that it comes from std.array, so I import std.array and try again using "auto buffer = appender!(ubyte[])();", and surely enough, now it does append correctly to the buffer. Great, I have a solution, so I go back to my project and implement it like I implemented it in my test code, but when I compile my project after this addition I get a new cryptic error message: "Error: __overloadset isn't a template". After digging a bit I realized that it's because in my project I also import std.file, apparently there are some collisions between std.bitmanip and std.file. Again it's solvable, but it's yet another fight with the language/standard library. I would also assume that it's not that uncommon for a module that use std.bitmanip to also use std.file, meaning that this error potentially may occur often. A bit on the side: It seems to me as importing std.bitmanip somehow adds new properties to my array (".read()" and ".write()", for example). Not necessarily a bad thing, more of "I've not seen this before, I was expecting that I were to concatenate the bytes from the conversion to my buffer using ~".Yes, some functions do overload one another's functions... Usually it's because they are templated, and can accept the same arguments. using std.file.read will work, or import stdfile = std.file; stdfile.read; Just makin sure ya knew how to fix that.
Mar 28 2013
On Thursday, 28 March 2013 at 20:24:50 UTC, Vidar Wahlberg wrote:To follow up with some new woes I'm currently struggling with: I'm storing some various values in an ubyte array. I discovered that it's probably std.bitmanip I wish to use in order to "convert" i.e. an int to 4 bytes (although I went first to std.conv looking for this feature). --snip--I am completely confused as to why you're doing what you are doing ... std.conv does work (and in the case you've listed, is unnecessary anyway). Try this: import std.stdio, std.conv; void main() { ubyte[] buffer; buffer ~= 5; // Simple solution buffer ~= to!ubyte(6); // Proper usage of "to" writeln(buffer); } --- Now, for performance reasons you might want to use appender (or buffer.reserve(n), if you happen to know how many items you'll be storing), but the simplest thing works.
Mar 28 2013
On Thursday, 28 March 2013 at 21:16:32 UTC, Chris Cain wrote:I am completely confused as to why you're doing what you are doing ... std.conv does work (and in the case you've listed, is unnecessary anyway). Try this: import std.stdio, std.conv; void main() { ubyte[] buffer; buffer ~= 5; // Simple solution buffer ~= to!ubyte(6); // Proper usage of "to" writeln(buffer); }This is not what I'm trying to achieve. This gives me an array with two elements, [5, 6]. What I want is to append the 4 bytes that make up one integer value, which using your values means buffer should hold a total of 8 bytes (two integers). H. S. Teoh answered well on how this can be achieved, although my feedback was not really meant as a question of "how is this done?", more of "why is this done like this, couldn't it be done much easier?".
Mar 28 2013
On Thursday, 28 March 2013 at 20:24:50 UTC, Vidar Wahlberg wrote:that it's probably std.bitmanip I wish to use in order to "convert" i.e. an int to 4 bytes (although I went first to std.conv looking for this feature).Ah, I reread it a couple of times and realized what you mean now. You want to turn an int like 5 into an array of ubytes like [0,0,0,5]. So, you were on the right track, here's one way on how it's done: --- import std.stdio, std.bitmanip, std.array; void main() { auto app = appender!(ubyte[])(); // Create an appender of type ubyte[] app.append!int(5); writeln(app.data()); } --- Without using appender, it's a bit more complicated: --- import std.stdio, std.bitmanip; void main() { auto buf = new ubyte[](4); buf.append!int(5); writeln(buf); } --- So, what append is doing is writing into the buffer, which must have enough space to put the int. So, thus, it must have 4 bytes. Guess what happens if we try to store a long in it? Yeah, it'll break on that as well. You'll also notice that subsequent calls to append on that buf will overwrite what's already in there. It's not obvious, but append needs either an array with enough space to store the element or an output range, like appender. Maybe the documentation could use a little work in this regard.
Mar 28 2013
On Thursday, 28 March 2013 at 20:24:50 UTC, Vidar Wahlberg wrote:A bit on the side: It seems to me as importing std.bitmanip somehow adds new properties to my array (".read()" and ".write()", for example). Not necessarily a bad thing, more of "I've not seen this before, I was expecting that I were to concatenate the bytes from the conversion to my buffer using ~".Sorry about the repeated postings ... I'm trying to read & answer it while also dealing with the norovirus :x --- import std.stdio : writeln; void main() { ubyte[] array; array ~= 5.toUbytes(); array ~= 6.toUbytes(); writeln(array); } ubyte[T.sizeof] toUbytes(T)(T val) safe { ubyte[T.sizeof] buf; std.bitmanip.append!T(buf[], val); return buf; } --- I see your latest post and see that you don't necessary care how to do it, but, I figured I might as well provide yet another way that's safe and simple. On Thursday, 28 March 2013 at 20:24:50 UTC, Vidar Wahlberg wrote:Great, I have a solution, so I go back to my project and implement it like I implemented it in my test code, but when I compile my project after this addition I get a new cryptic error message: "Error: __overloadset isn't a template". After digging a bit I realized that it's because in my project I also import std.file, apparently there are some collisions between std.bitmanip and std.file. Again it's solvable, but it's yet another fight with the language/standard library. I would also assume that it's not that uncommon for a module that use std.bitmanip to also use std.file, meaning that this error potentially may occur often.Yeah, that's a bit of an issue. Weird cryptic error. Oh well, as a general rule, try to import only the parts of the module you're actually using. This does two great things: it prevents namespace pollution causing errors like you've seen, and it documents WHERE someone has to look in order to find documentation on a function you're using in your module. As 1100110 noted, using a fully qualified ID is also a potential solution, especially if you only intend on using it in one place and the line isn't very noisy to begin with (as I showed in my example above)."why is this done like this, couldn't it be done much easier?".Maybe. What needs to be made easier and how would you suggest to fix it? The error message, certainly. Probably the documentation too. But the API itself seems sane to me in this instance, it just needs a better description.
Mar 28 2013
On Thursday, 28 March 2013 at 22:22:49 UTC, Chris Cain wrote:--- ubyte[T.sizeof] toUbytes(T)(T val) safe { ubyte[T.sizeof] buf; std.bitmanip.append!T(buf[], val); return buf; } ---This should be: ubyte[T.sizeof] toUbytes(T)(T val) safe { ubyte[T.sizeof] buf; import std.bitmanip : append; append!T(buf[], val); return buf; }
Mar 28 2013
On Thursday, 28 March 2013 at 22:22:49 UTC, Chris Cain wrote:Sorry about the repeated postings ... I'm trying to read & answer it while also dealing with the norovirus :xBeen there. Not amusing, I wish you well.Well, I'm not so proficient in the language yet that I'm going to climb to the top of Mount Stupid and say how it should be, because for what I know this may be perfectly logical with just me being blind to it, but to try to explain how it would make more sense to me: Since you got "ubyte[] buffer = [0, 0, 0, 0]; buffer.write!int(42); buffer.read!int();", I think it would be logical that "ubyte[] buffer; buffer.append!int(42); buffer.read!int()" would do pretty much the same (except instead of writing 4 bytes at index 0, it appends 4 bytes to the end of the array, then reads back the value). The latter code does however give you "Attempting to fetch the front of an empty array of ubyte". I don't get why you need to drag in std.array.appender() for std.bitmanip.append(), when you don't need it for read() and write()."why is this done like this, couldn't it be done much easier?".Maybe. What needs to be made easier and how would you suggest to fix it? The error message, certainly. Probably the documentation too. But the API itself seems sane to me in this instance, it just needs a better description.
Mar 28 2013
On Friday, March 29, 2013 00:38:25 Vidar Wahlberg wrote:I don't get why you need to drag in std.array.appender() for std.bitmanip.append(), when you don't need it for read() and write().Because read and write operate in place, and append doesn't. read and write operate on input ranges - read just reads the values from it as it iterates, and write overwrites what's there as it iterates. However, append uses an output range, because it's appending rather than overwriting, and for reasons that I don't understand, when treating an array as an output range, rather than appending (like an output range normally would), the put function (which is how stuff gets written to an output range) overwrites what's in the array rather than appending to it. So, using an empty array with any function operating on output ranges isn't going to work. Maybe there's a good reason for arrays working that way when they're treated as output ranges, but for me, it's a good reason to not use arrays when you need an output range. In either case, I'd suggest reading this if you want to know more about ranges: http://ddili.org/ders/d.en/ranges.html And since D's standard library ranges quite heavily, you're going to need at least a basic understanding of them if you want to use much of it. We really need a good article on ranges on dlang.org, but until we do, that link is probably your best resource for learning about ranges. - Jonathan M Davis
Mar 28 2013
On Friday, 29 March 2013 at 01:13:36 UTC, Jonathan M Davis wrote:In either case, I'd suggest reading this if you want to know more about ranges: http://ddili.org/ders/d.en/ranges.htmlThank you, I will read that (when the time is not 0400). I feel I need to stress that this is something that quite possibly will scare away newcomers, that static arrays, dynamic arrays and ranges looks very similar to each other, but behaves differently. This is not the first time I fall in this pit, and I suspect it's not the last time either. And well, sorry for continuing to nag about this, but take a look at the documentation for read(), write() and append() in std.bitmanip: T read(T, Endian endianness = Endian.bigEndian, R)(ref R range); void write(T, Endian endianness = Endian.bigEndian, R)(R range, T value, size_t index); void append(T, Endian endianness = Endian.bigEndian, R)(R range, T value); append() and write() are practically identical, just with write() having an extra parameter. The documentation even comes with examples for write() that use "ubyte[] buffer; buffer.write!ubyte(42, 0);", is it really odd that I assumed I could use append() in a similar matter when its parameters are exactly the same as for write() (minus the index)? Or is it unthinkable that I mistook arrays and Ranges for being interchangeable when the examples pass an array to a function that takes a Range? Ranges is something that's going to be new for a lot of people entering this language. When you know how arrays and ranges works in D I'm sure this makes perfect sense, but until you learn that, this is something that likely will confuse many people. Hopefully the article about ranges will clear things up for me.
Mar 28 2013
On Friday, March 29, 2013 04:01:40 Vidar Wahlberg wrote:Or is it unthinkable that I mistook arrays and Ranges for being interchangeable when the examples pass an array to a function that takes a Range?I don't think that it's unthinkable at all (and arrays _are_ ranges; it's just that in the case of output ranges, arrays act a bit oddly). The documentation should probably be improved to make such a mistake less likely.Ranges is something that's going to be new for a lot of people entering this language. When you know how arrays and ranges works in D I'm sure this makes perfect sense, but until you learn that, this is something that likely will confuse many people. Hopefully the article about ranges will clear things up for me.Yes. Ranges are incredibly powerful, but while the concept doesn't originate with D, AFAIK, actually using them in a serious API (particularly in a standard library) is unique to D. And our lack of good tutorials on them is probably our biggest documentation problem. It definitely needs to be fixed. - Jonathan M Davis
Mar 28 2013
Definitely need to add some updates to the docs. Long story: D provides an iterable interface called a Range. There are two base forms, inputRange and outputRange. Dynamic Arrays have the privilege of being, a inputRange, outputRange, and a container. An array however doesn't operate as you might expect, especially when using a function called append on it. An output range consists of the ability call put for Range and Element (defined in std.range) for a dynamic array this means you can assign to front. ubyte[] buffer; buffer.append!ubyte(42); The append function takes an outputRange, if we drill down the call that would be made (ignoring my value isn't correct) buffer.front = 42; buffer.popFront(); Thus when using an array as an outputRange it 1) Must have a size (hence the error: "Attempting to fetch the front of an empty array of ubyte") 2) Starts at the beginning (hence the observation: "instead of appending it behaves like write()") 3) Is consumed (You didn't run into this) That is why arrays are awkward and the example makes use of appender (a more traditional form of an outputRange) ------------ The write function seems a little odd as it uses random access (indexing). Instead of assigning to front like append does it assigns at index buffer[0]... ------------ The implementation of append is what you will find more in idiomatic D. In fact if the module was written today write wouldn't exist and append would probably have been named write. ------------- ------------- "Error: __overloadset isn't a template" That needs fixed, it usually does a better job of specifying conflicting modules across modules. ------------ ------------ "It seems to me as importing std.bitmanip somehow adds new properties" D provides UFCS (Uniform Function Call Syntax). For a given type A, foo(A a) is callable in both foo(a) and a.foo(). (Historical note: UFCS is recent addition, a bug allowed it to work with dynamic arrays like you see in these docs)
Mar 28 2013
On Friday, March 29, 2013 03:11:08 Jesse Phillips wrote:The write function seems a little odd as it uses random access (indexing). Instead of assigning to front like append does it assigns at index buffer[0]...That's because it's operating on multiple bytes at a time (e.g. writing the 4 bytes of an int at once). Really, it's written for arrays and was generalized because it could be rather than really having been written for ranges.The implementation of append is what you will find more in idiomatic D. In fact if the module was written today write wouldn't exist and append would probably have been named write.That could be argued for, but write and append do different things and both exist for a reason. Only having append would actually be problematic, as there are cases where you really do need write and not append. And neither of them have been in Phobos for all that long (peek, read, write, and append were added in 2.060). - Jonathan M Davis
Mar 28 2013
On Friday, 29 March 2013 at 02:39:59 UTC, Jonathan M Davis wrote:Only having append would actually be problematic, as there are cases where you really do need write and not append. And neither of them have been in Phobos for all that long (peek, read, write, and append were added in 2.060). - Jonathan M DavisAh, I was just going by the docs when I wrote this. I apparently got it wrong. write doesn't using indexing it uses opSliceAssign (which isn't in its constraints). I'm also not familiar with the use cases and there are definitely reasons to stray from idiomatic D.
Mar 29 2013
On Friday, March 29, 2013 16:33:33 Jesse Phillips wrote:Ah, I was just going by the docs when I wrote this. I apparently got it wrong. write doesn't using indexing it uses opSliceAssign (which isn't in its constraints).I'll have to take a look at that. I think that it was pretty much assuming that all of the slicing stuff would work with hasSlicing, but that stuff doesn't check for opSliceAssign, so clearly the template constraints need some work. Thanks for pointing that out. - Jonathan M Davis
Mar 29 2013
On Wednesday, 27 March 2013 at 15:34:20 UTC, Vidar Wahlberg wrote:I know I'm probably going to upset some people with this, bashing their favourite child and all, but I wanted to let you know the experience I've had with D so far, as a novice D coder with a heavy Java & light C++ background. It's not that I dislike D, in fact there are tons of things I love about it, it's pretty much exactly what I'm looking for in a programming language at the moment. Yet, I encounter some frustrating issues when coding, often leaving me with the impression that I'm fighting the language more than the problem I'm trying to solve. True, there are many things I don't know about D, compilers or the inner workings of a computer, and some of the fights I have with the language are likely started by myself because I'm dragging along my bias from other languages, drawing misconceptions on how the D language actually works. My intentions are not to insult, but shed some light on some of the difficulties I've faced (and how I solved them), with the hope that it will help others from facing the same difficulties.That is fine don't worry. Knowing what people have trouble with when starting with D is very valuable IMO.Woes: ----- - I find myself in a world of pain when I want to share data more complex than the basic data types (int, char, byte, etc) across threads. Seemingly the magic trick is to "cast(shared) foo" (or "cast(immutable)") when passing objects/references to another thread, then "cast(Foo)" back on the receiving end (as most classes/structs in the standard library refuse to let you call any methods when the object is shared). The examples in the source and TDPL are fairly limited on the issue, it mostly covers only those basic data types.Well, that is a long standing issue. shared is poorly defined and inconsistently implemented. Sad, but true.- While the "auto"-keyword often is great, it can lead to difficulties, especially when used as the return type of a function, such as "auto foo() { return bar; }". Sometimes you may wish to store the result of a function/method call as a global variable/class member, but when the function/method returns "auto" it's not apparent what the data type may be. While you may be able to find out what "bar" is by digging in the source code, it can still be difficult to find. One example is to save the result of "std.regex.match()" as a member in a class. For me the solution was to "import std.traits", create a function "auto matchText(string text) { return match(text, myRegex); }" and define the class member as "ReturnType!matchText matchResult;" (do also note that function & member must come in the right order for this to compile). This was all but obvious to a novice D coder as myself, the solution was suggested to me in the IRC channel.Yes, I have to say that it is a pain sometime. Additionally, it have some rough edges you may want to know : - Function that never return are inferred void. I would have preferred typeof(null) as void lead to many static and repetitive code for nothing when doing metaprograming. - Type inference handle very poorly recursion. It should simply exclude the recursion when doing type inference as it won't change the return type. The error message can be very opaque. - In some cases, you have to add explicit casts when implicit would have been enough in theory (but the type inference mechanism is confused). Back to your issue, you may want to use typeof . Sometime, it is plain better as type can become complex when doing metaprogramming (and phobos is full of this). You can also use an alias in order to make things look nice. alias FooT = typeof(foo()); FooT bar; bar = foo(); // Happy ?Gotchas: -------- - The lack of "rectangular" arrays created at runtime in D ("int i = 5; int[i][i] foo;") can be quite confusing for programmers with Java or C++ background. Even though there exists alternatives (http://denis-sh.github.com/phobos-additions/unstd.multidim nsionalarray.html), this design decision and how to get around it when you really desire a "rectangular" array could be explained in more detail at http://dlang.org/arrays.html. - Static array versus dynamic array was one of the first traps I stepped on (http://forum.dlang.org/thread/jnu1an$rjr$1 digitalmars.com). Until Jonathan M. Davis explained it in detail (http://d.puremagic.com/issues/show_bug.cgi?id=8026#c4), I pretty much considered it as "magic" that "randomShuffle(staticArray);" did not sort the array while "randomShuffle(staticArray[]);" did (the first call now gives you an compile error, though). That static arrays are value types while dynamic arrays are reference types may not be obvious for those with primarily Java background.Yes, Java have no value types, only reference types. This is actually a performance issue for 2 reasons : - You chase pointers, so you have cache miss and this is very costly on modern CPUs. - This is very GC unfriendly. For instance, on a project at a previous work, we had an LRU cache into an application. We knew that most object should traverse the cache and get out very fast, when other will stick in it most of the time for reason that'd be too long to explain. It lead to difficult to solve performances issues as while going through the LRU, object were considered as old by the GC and treated differently. As a consequence, the application generated a lot of garbage considered as old by the GC (when the GC assume that most objects dies young). It required a lot of work to make that work, when using value type would have solved the whole this instantly.- When casting a value to an enum, there's no checking that the value actually is a valid enum value. Don't think I ever found a solution on how to check whether the value after casting is a valid enum value, it hasn't been a pressing issue.Very true. enum as they stand have some safety issue IMO.- Compiling where DMD can't find all modules cause a rather cryptic error message. A solution is to make sure you specify all source files when compiling.dmd is really uneven with error messages. Some are purely awesome, and other are completely cryptic. A very "funny" one is when you use import instead of module (dmd complain about the module conflicting with itself).Wishlist: --------- - "void[T]" associative array (i.e. a "set") would be nice, can be achieved with "byte[0][T]".Don't get me started on AA !- "Foo foo = new Foo();" for global variables/class members. Now you must "Foo foo; static this() { foo = new Foo(); }".Yes, as it imply an heap allocation. It is an harder problem that it seems as all thoses object could reference themselves.
Mar 27 2013
On 03/27/2013 05:04 PM, deadalnix wrote:On Wednesday, 27 March 2013 at 15:34:20 UTC, Vidar Wahlberg wrote:I strongly disagree. What would be an example of the problems you are apparently experiencing?...- While the "auto"-keyword often is great, it can lead to difficulties, especially when used as the return type of a function, such as "auto foo() { return bar; }". Sometimes you may wish to store the result of a function/method call as a global variable/class member, but when the function/method returns "auto" it's not apparent what the data type may be. While you may be able to find out what "bar" is by digging in the source code, it can still be difficult to find. One example is to save the result of "std.regex.match()" as a member in a class. For me the solution was to "import std.traits", create a function "auto matchText(string text) { return match(text, myRegex); }" and define the class member as "ReturnType!matchText matchResult;" (do also note that function & member must come in the right order for this to compile). This was all but obvious to a novice D coder as myself, the solution was suggested to me in the IRC channel.Yes, I have to say that it is a pain sometime. Additionally, it have some rough edges you may want to know : - Function that never return are inferred void. I would have preferred typeof(null) as void lead to many static and repetitive code for nothing when doing metaprograming.- Type inference handle very poorly recursion. It should simply exclude the recursion when doing type inference as it won't change the return type. The error message can be very opaque.The cases that are allowed would need to be specified more rigorously.- In some cases, you have to add explicit casts when implicit would have been enough in theory (but the type inference mechanism is confused). ...Just serialize the CTFE object graph into the static data segment.- "Foo foo = new Foo();" for global variables/class members. Now you must "Foo foo; static this() { foo = new Foo(); }".Yes, as it imply an heap allocation. It is an harder problem that it seems as all thoses object could reference themselves.
Mar 27 2013
On Wednesday, 27 March 2013 at 17:23:01 UTC, Timon Gehr wrote:On 03/27/2013 05:04 PM, deadalnix wrote:I believe that's what this pull aims to do: https://github.com/D-Programming-Language/dmd/pull/1724On Wednesday, 27 March 2013 at 15:34:20 UTC, Vidar WahlbergJust serialize the CTFE object graph into the static data segment.... - "Foo foo = new Foo();" for global variables/class members. Now you must "Foo foo; static this() { foo = new Foo(); }".Yes, as it imply an heap allocation. It is an harder problem that it seems as all thoses object could reference themselves.
Mar 27 2013
On 03/27/2013 06:34 PM, Brad Anderson wrote:On Wednesday, 27 March 2013 at 17:23:01 UTC, Timon Gehr wrote:Almost. It does not support TLS, which is a severe limitation.On 03/27/2013 05:04 PM, deadalnix wrote:I believe that's what this pull aims to do: https://github.com/D-Programming-Language/dmd/pull/1724On Wednesday, 27 March 2013 at 15:34:20 UTC, Vidar WahlbergJust serialize the CTFE object graph into the static data segment.... - "Foo foo = new Foo();" for global variables/class members. Now you must "Foo foo; static this() { foo = new Foo(); }".Yes, as it imply an heap allocation. It is an harder problem that it seems as all thoses object could reference themselves.
Mar 27 2013
On Wednesday, 27 March 2013 at 17:23:01 UTC, Timon Gehr wrote:I strongly disagree. What would be an example of the problems you are apparently experiencing?T foo(alias fallback)() { // Do some processing return the result. If an error occurs use fallback mechanism. } Reasonable thing to do as fallback is to try another method workaround the error, throw, whatever. The problem is that the fallback type inference makes it painful to work with, especially if fallback is a template itself. For instance, in SDC, you can parse ambiguous things as follow : parseTypeOrExpression!((parsed) { static if(is(typeof(parsed) : Expression)) { // Do something } else { throw SomeException(); } })(tokenRange); This is bound to fail. When a function never return, it make no sens to force a type on it and the magic subtype typeof(null) should be used (as typeof(null) can cast to anything, it is valid to call the function in any condition).
Mar 27 2013
On Wed, 27 Mar 2013 18:46:09 +0100 "deadalnix" <deadalnix gmail.com> wrote:On Wednesday, 27 March 2013 at 17:23:01 UTC, Timon Gehr wrote:A "does not return" return type would be nice for other things anyway. For example: void error(string s) // Convenience helper { throw new MyException("blah blah blah: "~s); } void serveFilesForever() { while(true) listenAndRespond(); } int doStuff() { if(blah) return 1; else if(blah2) { // Bullshit compile error: // "Not all paths return a value" error("poop"); // Must add dead code, keep compiler happy: //assert(0); } else { // Same bullshit serveFilesForever(); //assert(0); } } Compare to: no_return error() // Convenience helper { //if(foo) return; // Oops! But compiler catches error. throw new MyException("blah blah blah"); } no_return serveFilesForever() { // This might be harder for the compiler to check :( while(true) listenAndRespond(); } int doStuff() { if(blah) return 1; else if(blah2) error("poop"); // No bullshit, just works else serveFilesForever(); // Whee! }I strongly disagree. What would be an example of the problems you are apparently experiencing?T foo(alias fallback)() { // Do some processing return the result. If an error occurs use fallback mechanism. } Reasonable thing to do as fallback is to try another method workaround the error, throw, whatever. The problem is that the fallback type inference makes it painful to work with, especially if fallback is a template itself. For instance, in SDC, you can parse ambiguous things as follow : parseTypeOrExpression!((parsed) { static if(is(typeof(parsed) : Expression)) { // Do something } else { throw SomeException(); } })(tokenRange); This is bound to fail. When a function never return, it make no sens to force a type on it and the magic subtype typeof(null) should be used (as typeof(null) can cast to anything, it is valid to call the function in any condition).
Mar 27 2013
On 03/27/2013 06:46 PM, deadalnix wrote:On Wednesday, 27 March 2013 at 17:23:01 UTC, Timon Gehr wrote:I see. What is needed is a way to specify that a function does never return. (eg. a bottom type)I strongly disagree. What would be an example of the problems you are apparently experiencing?T foo(alias fallback)() { // Do some processing return the result. If an error occurs use fallback mechanism. } Reasonable thing to do as fallback is to try another method workaround the error, throw, whatever. The problem is that the fallback type inference makes it painful to work with, especially if fallback is a template itself. For instance, in SDC, you can parse ambiguous things as follow : parseTypeOrExpression!((parsed) { static if(is(typeof(parsed) : Expression)) { // Do something } else { throw SomeException(); } })(tokenRange);This is bound to fail. When a function never return, it make no sens to force a type on it and the magic subtype typeof(null) should be used (as typeof(null) can cast to anything, it is valid to call the function in any condition).It cannot cast to everything.
Mar 27 2013
I'm impressed and most grateful for the feedback, I've learned some new things today :)int i = 5; auto foo = new int[][](i,i);Won't this create an array (with 5 elements) of arrays (with 5 elements), also called a "jagged array"? Where memory is not necessarily continuously allocated and looking up values adds another layer of indirection?It is much more simple actually, "typeof(match(string.init, Regex.init)) variable;" and no extra functions or source digging is needed.Many of you pointed this out, thanks, this a better solution than what I had. I needed to write "Regex!char.init" and not just "Regex.init", but that's just a minor detail.Yea, I'd imagine there would be some value-type/reference-type confusion from a lot of newcomers just because D *has* both value types and reference types. As opposed to, say, Java where (almost?) everything is a reference type, or C++ where everything is a value type, etc. Personally, I find it very much worthwhile to have both value and reference types. But you're right it is something many people will have to learn to get used to, and particularly so with arrays.I find it quite nice that you have both value and reference types, and for the most part it's rather clear in D when you're dealing with a reference and when you're dealing with a value. It was just arrays that caught me off guard, and I think others with a similar background may do the same mistake, so my comment about this really just is "arrays may require more explanation aimed at Java developers" :)But D has an easy solution - just use RDMD instead: rdmd --build-only -I{include paths as usual} {other flags} main.dThat's a good tip! Somehow I had the notion that rdmd was purely a tool for "scripting", as in dynamically parsing code (like Python, Perl, etc), so I never looked much into it.
Mar 27 2013
On Wed, Mar 27, 2013 at 07:06:45PM +0100, Vidar Wahlberg wrote: [...]Yeah, the documentation needs to be improved. Maybe file an enhancement bug for this at d.puremagic.com/issues ?Yea, I'd imagine there would be some value-type/reference-type confusion from a lot of newcomers just because D *has* both value types and reference types. As opposed to, say, Java where (almost?) everything is a reference type, or C++ where everything is a value type, etc. Personally, I find it very much worthwhile to have both value and reference types. But you're right it is something many people will have to learn to get used to, and particularly so with arrays.I find it quite nice that you have both value and reference types, and for the most part it's rather clear in D when you're dealing with a reference and when you're dealing with a value. It was just arrays that caught me off guard, and I think others with a similar background may do the same mistake, so my comment about this really just is "arrays may require more explanation aimed at Java developers" :)rdmd gives D a scripting-like interface, but D is inherently a compiled language, so it isn't actually a D interpreter. :) It's just that D compilation (esp. with DMD) is incredibly fast, given what it does, so that calling rdmd is almost like "interpreting" D code on-the-fly. What it actually does, of course, is to compile the code and cache the compiled objects, so running it multiple times does not repeatedly incur the compile-time overhead. T -- Caffeine underflow. Brain dumped.But D has an easy solution - just use RDMD instead: rdmd --build-only -I{include paths as usual} {other flags} main.dThat's a good tip! Somehow I had the notion that rdmd was purely a tool for "scripting", as in dynamically parsing code (like Python, Perl, etc), so I never looked much into it.
Mar 27 2013
On Wed, 27 Mar 2013 11:13:31 -0700 "H. S. Teoh" <hsteoh quickfur.ath.cx> wrote:On Wed, Mar 27, 2013 at 07:06:45PM +0100, Vidar Wahlberg wrote: [...]Yea, the arrays definitely do blur the value/reference lines. Aside from static/dynamic, another example of this is how a dynamic array's *values* are reference, but it's length is by-value. Of course, it's very simple when you realize that a D dynamic array is more-or-less like this: struct Array(T) { size_t length; T* ptr; } But still, it's definitely something to get used to.I find it quite nice that you have both value and reference types, and for the most part it's rather clear in D when you're dealing with a reference and when you're dealing with a value. It was just arrays that caught me off guard, and I think others with a similar background may do the same mistake, so my comment about this really just is "arrays may require more explanation aimed at Java developers" :)It was originally designed for scripting uses. But making that work well required adding the feature of "*automatically* detect and compile all required sources". And that feature turned out to be very useful just for its own sake, so RDMD grew into something that could nicely handle both.But D has an easy solution - just use RDMD instead: rdmd --build-only -I{include paths as usual} {other flags} main.dThat's a good tip! Somehow I had the notion that rdmd was purely a tool for "scripting", as in dynamically parsing code (like Python, Perl, etc), so I never looked much into it.rdmd gives D a scripting-like interface, but D is inherently a compiled language, so it isn't actually a D interpreter. :)Sure it is! It's an AOT interpreter! (Which is ironically something very well-respected and sought-after in interpreted-language circles. Go figure: they've reinvented native compilation and simply gave it a new name.)
Mar 27 2013
On Wednesday, 27 March 2013 at 18:06:46 UTC, Vidar Wahlberg wrote:I'm impressed and most grateful for the feedback, I've learned some new things today :)Unfortunately yes. However, it's not a hard problem to overcome with a wrapper struct over a contiguous array.int i = 5; auto foo = new int[][](i,i);Won't this create an array (with 5 elements) of arrays (with 5 elements), also called a "jagged array"? Where memory is not necessarily continuously allocated and looking up values adds another layer of indirection?
Mar 27 2013
On Wed, Mar 27, 2013 at 07:41:42PM +0100, John Colvin wrote:On Wednesday, 27 March 2013 at 18:06:46 UTC, Vidar Wahlberg wrote:Which is what Denis' multidimensional array implementation does. As does my implementation as well. This seems to be quite a common use-case; we should put this into Phobos IMO. T -- People tell me that I'm skeptical, but I don't believe it.I'm impressed and most grateful for the feedback, I've learned some new things today :)Unfortunately yes. However, it's not a hard problem to overcome with a wrapper struct over a contiguous array.int i = 5; auto foo = new int[][](i,i);Won't this create an array (with 5 elements) of arrays (with 5 elements), also called a "jagged array"? Where memory is not necessarily continuously allocated and looking up values adds another layer of indirection?
Mar 27 2013
On 3/27/13 3:18 PM, H. S. Teoh wrote:Which is what Denis' multidimensional array implementation does. As does my implementation as well. This seems to be quite a common use-case; we should put this into Phobos IMO.Agree. Do you (or Denis) have something in reviewable form? Andrei
Mar 27 2013
On Wed, Mar 27, 2013 at 04:28:16PM -0400, Andrei Alexandrescu wrote:On 3/27/13 3:18 PM, H. S. Teoh wrote:Here is Denis' implementation: https://github.com/denis-sh/phobos-additions/blob/master/unstd/multidimensionalarray.d As I didn't write the code, I can't say how review-ready it is. My own implementation is somewhat incomplete at this time, so it's not quite ready for review yet. It's missing some functionality that Denis' implementation has (arbitrary index reordering, more complete range-based access, opApply, etc.). T -- EMACS = Extremely Massive And Cumbersome SystemWhich is what Denis' multidimensional array implementation does. As does my implementation as well. This seems to be quite a common use-case; we should put this into Phobos IMO.Agree. Do you (or Denis) have something in reviewable form?
Mar 27 2013
28.03.2013 0:28, Andrei Alexandrescu пишет:On 3/27/13 3:18 PM, H. S. Teoh wrote:No. The module is 2 years old and I only made changes on dmd requests. But if such functionality is really needed I will be happy to revise API and prepare my implementation for review. P.S. It's an accident I have seen this post. I think in the case of such questions it is obligatory to e-mail the author or there is a big risk the question will bot be delivered. -- Денис В. Шеломовский Denis V. ShelomovskijWhich is what Denis' multidimensional array implementation does. As does my implementation as well. This seems to be quite a common use-case; we should put this into Phobos IMO.Agree. Do you (or Denis) have something in reviewable form? Andrei
Oct 08 2013
On 3/27/13 1:23 PM, Timon Gehr wrote:Ideally such function should return a "none" type, the bottom of the hierarchy lattice. We don't have such, so returning typeof(null) (which we do have) is the next best choice as it's just above bottom. Andrei- Function that never return are inferred void. I would have preferred typeof(null) as void lead to many static and repetitive code for nothing when doing metaprograming.I strongly disagree.
Mar 27 2013
On 03/27/2013 07:20 PM, Andrei Alexandrescu wrote:On 3/27/13 1:23 PM, Timon Gehr wrote:Maybe it is one next best choice. It is still a horrible choice. It's not any more just above bottom than the following type: struct JustAboveBottom{ } We should either go with the real thing or do nothing about it.Ideally such function should return a "none" type, the bottom of the hierarchy lattice. We don't have such, so returning typeof(null) (which we do have) is the next best choice as it's just above bottom. Andrei- Function that never return are inferred void. I would have preferred typeof(null) as void lead to many static and repetitive code for nothing when doing metaprograming.I strongly disagree.
Mar 27 2013
On Wednesday, 27 March 2013 at 18:20:49 UTC, Andrei Alexandrescu wrote:On 3/27/13 1:23 PM, Timon Gehr wrote:I thought that typeof(null) was that bottom type. What is the difference ? Anyway, void isn't the right choice here and is a pain to work with.Ideally such function should return a "none" type, the bottom of the hierarchy lattice. We don't have such, so returning typeof(null) (which we do have) is the next best choice as it's just above bottom.- Function that never return are inferred void. I would have preferred typeof(null) as void lead to many static and repetitive code for nothing when doing metaprograming.I strongly disagree.
Mar 27 2013
On 03/28/2013 04:18 AM, deadalnix wrote:On Wednesday, 27 March 2013 at 18:20:49 UTC, Andrei Alexandrescu wrote:There is a huge difference. - typeof(null) is a subtype of all _class, interface and pointer_ types because they all _include_ its value, null. - bottom is a subtype of _all_ types, because there is _no_ value of type bottom.On 3/27/13 1:23 PM, Timon Gehr wrote:I thought that typeof(null) was that bottom type. What is the difference ?Ideally such function should return a "none" type, the bottom of the hierarchy lattice. We don't have such, so returning typeof(null) (which we do have) is the next best choice as it's just above bottom.- Function that never return are inferred void. I would have preferred typeof(null) as void lead to many static and repetitive code for nothing when doing metaprograming.I strongly disagree.Anyway, void isn't the right choice here and is a pain to work with.typeof(null) would be worse.
Mar 28 2013
On Thursday, 28 March 2013 at 10:34:35 UTC, Timon Gehr wrote:On 03/28/2013 04:18 AM, deadalnix wrote:OK I see the difference.On Wednesday, 27 March 2013 at 18:20:49 UTC, Andrei Alexandrescu wrote:There is a huge difference. - typeof(null) is a subtype of all _class, interface and pointer_ types because they all _include_ its value, null. - bottom is a subtype of _all_ types, because there is _no_ value of type bottom.On 3/27/13 1:23 PM, Timon Gehr wrote:I thought that typeof(null) was that bottom type. What is the difference ?Ideally such function should return a "none" type, the bottom of the hierarchy lattice. We don't have such, so returning typeof(null) (which we do have) is the next best choice as it's just above bottom.- Function that never return are inferred void. I would have preferred typeof(null) as void lead to many static and repetitive code for nothing when doing metaprograming.I strongly disagree.I don't see how it is worse.Anyway, void isn't the right choice here and is a pain to work with.typeof(null) would be worse.
Mar 28 2013
Welcome and thanks for sharing your experience! Few (hopefully) useful hints:----- - I find myself in a world of pain when I want to share data more complex than the basic data types (int, char, byte, etc) across threads. Seemingly the magic trick is to "cast(shared) foo" (or "cast(immutable)") when passing objects/references to another thread, then "cast(Foo)" back on the receiving end (as most classes/structs in the standard library refuse to let you call any methods when the object is shared). The examples in the source and TDPL are fairly limited on the issue, it mostly covers only those basic data types.That is somewhat intended as D proposes message-passing a "default" multi-threading approach and makes sharing global data intentionally difficult. What lacks though, is some solid article series about how "D way" of doing things here, it is often quite not obvious. Definitely an area for improvement.- While the "auto"-keyword often is great, it can lead to difficulties, especially when used as the return type of a function, such as "auto foo() { return bar; }". Sometimes you may wish to store the result of a function/method call as a global variable/class member, but when the function/method returns "auto" it's not apparent what the data type may be. While you may be able to find out what "bar" is by digging in the source code, it can still be difficult to find. One example is to save the result of "std.regex.match()" as a member in a class. For me the solution was to "import std.traits", create a function "auto matchText(string text) { return match(text, myRegex); }" and define the class member as "ReturnType!matchText matchResult;" (do also note that function & member must come in the right order for this to compile). This was all but obvious to a novice D coder as myself, the solution was suggested to me in the IRC channel.It is much more simple actually, "typeof(match(string.init, Regex.init)) variable;" and no extra functions or source digging is needed. D static introspection is so much more powerful than in other languages that is often completely overlooked by newcomers.- Static array versus dynamic array was one of the first traps I stepped on (http://forum.dlang.org/thread/jnu1an$rjr$1 digitalmars.com). Until Jonathan M. Davis explained it in detail (http://d.puremagic.com/issues/show_bug.cgi?id=8026#c4), I pretty much considered it as "magic" that "randomShuffle(staticArray);" did not sort the array while "randomShuffle(staticArray[]);" did (the first call now gives you an compile error, though). That static arrays are value types while dynamic arrays are reference types may not be obvious for those with primarily Java background.Unfortunately, there is a conflict of interest when targeting both programmers with Java and C background at the same time, what D tries to do. Sometimes it is very hard to resolve right.- When casting a value to an enum, there's no checking that the value actually is a valid enum value. Don't think I ever found a solution on how to check whether the value after casting is a valid enum value, it hasn't been a pressing issue.You most likely want std.conv.to - Phobos lexical cast. It does check enum valid values.- Compiling where DMD can't find all modules cause a rather cryptic error message. A solution is to make sure you specify all source files when compiling.Have you tried rdmd?
Mar 27 2013
On Wednesday, 27 March 2013 at 15:34:20 UTC, Vidar Wahlberg wrote:- While the "auto"-keyword often is great, it can lead to difficulties, especially when used as the return type of a function, such as "auto foo() { return bar; }". Sometimes you may wish to store the result of a function/method call as a global variable/class member, but when the function/method returns "auto" it's not apparent what the data type may be. While you may be able to find out what "bar" is by digging in the source code, it can still be difficult to find. One example is to save the result of "std.regex.match()" as a member in a class. For me the solution was to "import std.traits", create a function "auto matchText(string text) { return match(text, myRegex); }" and define the class member as "ReturnType!matchText matchResult;" (do also note that function & member must come in the right order for this to compile). This was all but obvious to a novice D coder as myself, the solution was suggested to me in the IRC channel.This is actually nothing to do with auto. It's endemic to all templated returns from functions and the same is true in c++ (I don't know enough about java generics to comment). A solution to your particular problem class A(type_of_myRegex) { alias ReturnType!(match!(string, type_of_myRegex)) RegexR; RegexR r; void foo(string text, type_of_myRegex myRegex) { r = match(text, myRegex); } } of course, if you only ever use one Regex type then of course you could do without the templating in the class and hard-code it in.
Mar 27 2013
On Wednesday, 27 March 2013 at 16:15:45 UTC, John Colvin wrote:This is actually nothing to do with auto. It's endemic to all templated returns from functions and the same is true in c++ (I don't know enough about java generics to comment).Sorry, I'd want to correct this to "This is only partly to do with auto".
Mar 27 2013
On Wed, 27 Mar 2013 16:34:19 +0100 "Vidar Wahlberg" <vidar.wahlberg gmail.com> wrote:=20 Woes: ----- - I find myself in a world of pain when I want to share data more=20 complex than the basic data types (int, char, byte, etc) across=20 threads. Seemingly the magic trick is to "cast(shared) foo" (or=20 "cast(immutable)") when passing objects/references to another=20 thread, then "cast(Foo)" back on the receiving end (as most=20 classes/structs in the standard library refuse to let you call=20 any methods when the object is shared). The examples in the=20 source and TDPL are fairly limited on the issue, it mostly covers=20 only those basic data types. =20I haven't been dealing with threads or "shared", but my understanding is that casting to and from shared is not recommended as it (deliberately) subverts the safety checks in the type system. Although can't really say what the right way to handle it is since, as I said, I've never dealt with that part of the language.- While the "auto"-keyword often is great, it can lead to=20 difficulties, especially when used as the return type of a=20 function, such as "auto foo() { return bar; }". Sometimes you may=20 wish to store the result of a function/method call as a global=20 variable/class member, but when the function/method returns=20 "auto" it's not apparent what the data type may be. While you may=20 be able to find out what "bar" is by digging in the source code,=20Here's the trick I like to use: // Some var with unknown type auto var =3D ...; pragma(msg, "var: " ~ typeof(var).stringof); That will print something like this at compile-time: var: int Unfortunately (or fortunately, depending on your point of view), it ignores all convenience aliases and will always give you the *full* original concrete static type, instead of any prettied-up aliases for it, but it does work and gets the job done.=20 - Static array versus dynamic array was one of the first traps I=20 stepped on=20 (http://forum.dlang.org/thread/jnu1an$rjr$1 digitalmars.com).=20 Until Jonathan M. Davis explained it in detail=20 (http://d.puremagic.com/issues/show_bug.cgi?id=3D8026#c4), I pretty=20 much considered it as "magic" that "randomShuffle(staticArray);"=20 did not sort the array while "randomShuffle(staticArray[]);" did=20 (the first call now gives you an compile error, though). That=20 static arrays are value types while dynamic arrays are reference=20 types may not be obvious for those with primarily Java background. =20Yea, I'd imagine there would be some value-type/reference-type confusion from a lot of newcomers just because D *has* both value types and reference types. As opposed to, say, Java where (almost?) everything is a reference type, or C++ where everything is a value type, etc. Personally, I find it very much worthwhile to have both value and reference types. But you're right it is something many people will have to learn to get used to, and particularly so with arrays.- When casting a value to an enum, there's no checking that the=20 value actually is a valid enum value. Don't think I ever found a=20 solution on how to check whether the value after casting is a=20 valid enum value, it hasn't been a pressing issue. =20Honestly, I hate that, too. The problem is that enum is (unfortunately) intended to do double-duty as a bitfield so you can do something like this: enum Options { FeatureA =3D 0b0000_0001; FeatureB =3D 0b0000_0010; FeatureC =3D 0b0000_0100; FeatureD =3D 0b0000_1000; // etc... } // Use features A and C auto myOptions =3D Options.FeatureA | Options.FeatureC; That possibility means that D *can't* check for validity as you suggest. I'm convinced that scenario *should* be considered an entirely separate thing because cramming it together with regular enumerations creates conflicting goals with the two usages of "enum", and forces unfortunate design compromises with both.- Compiling where DMD can't find all modules cause a rather=20 cryptic error message. A solution is to make sure you specify all=20 source files when compiling. =20D uses the C/C++ compilation model: It doesn't compile any files you don't specifically tell it to compile. Remember, importing is not the same as compiling: Merely importing a file only makes the symbols visible, it doesn't automatically *compile* the imported file. You have to tell it to do that. Non-C/C++ developers often get tripped up on this, but it's normal and expected for C/C++ toolchains or really anything that uses a separate "link" step. But D has an easy solution - just use RDMD instead: rdmd --build-only -I{include paths as usual} {other flags} main.d Just give it your *main* D file (*must* be the *last* argument), and it'll automatically find all .d files needed and pass them all to DMD.=20 Wishlist: --------- - "void[T]" associative array (i.e. a "set") would be nice, can=20 be achieved with "byte[0][T]". =20I agree. I've just been using "bool[T]", but "byte[0][T]" is a good idea. Could probably do this, too, to help out: template HashSet(T) { alias byte[0][T] HashSet; } Then just "HashSet!int", "HashSet!string", "HashSet!Foo", etc.- "Foo foo =3D new Foo();" for global variables/class members. Now=20 you must "Foo foo; static this() {=A0foo =3D new Foo();=A0}".IMO, it's better to do lazy initialization: private Foo _foo; private bool fooInited; property ref Foo foo() { if(!_foo && !fooInited) { _foo =3D new Foo(); fooInited =3D true; } return _foo; } I prefer to avoid "static this()" because that makes it easy to end up with a "cyclic dependency" error on program startup. If two modules that import each other (directly or indirectly) both have a "static this()", then the runtime has no way to know which needs to run first, so it gives a cyclic dependency error instead. This lazy-initialization avoids that problem. Of course, it certainly doesn't help with the convenience issue. But all that boilerplate could all be wrapped up in a reusable convenience mixin. I think that would be a great thing to have in Phobos.
Mar 27 2013
On Wed, Mar 27, 2013 at 12:52:25PM -0400, Nick Sabalausky wrote:On Wed, 27 Mar 2013 16:34:19 +0100 "Vidar Wahlberg" <vidar.wahlberg gmail.com> wrote:[...]What's wrong with using typeof? auto var = ...; typeof(var) anotherVar; ... anotherVar = var; [...]- While the "auto"-keyword often is great, it can lead to difficulties, especially when used as the return type of a function, such as "auto foo() { return bar; }". Sometimes you may wish to store the result of a function/method call as a global variable/class member, but when the function/method returns "auto" it's not apparent what the data type may be. While you may be able to find out what "bar" is by digging in the source code,Here's the trick I like to use: // Some var with unknown type auto var = ...; pragma(msg, "var: " ~ typeof(var).stringof); That will print something like this at compile-time: var: int Unfortunately (or fortunately, depending on your point of view), it ignores all convenience aliases and will always give you the *full* original concrete static type, instead of any prettied-up aliases for it, but it does work and gets the job done.As a compromise for now, I'd just use std.conv.to for when I want enum values checked. In any case, using casts should be avoided in D unless there's no other way around it. Casting is a system-level operation, and most application code shouldn't be using it.- When casting a value to an enum, there's no checking that the value actually is a valid enum value. Don't think I ever found a solution on how to check whether the value after casting is a valid enum value, it hasn't been a pressing issue.Honestly, I hate that, too. The problem is that enum is (unfortunately) intended to do double-duty as a bitfield so you can do something like this: enum Options { FeatureA = 0b0000_0001; FeatureB = 0b0000_0010; FeatureC = 0b0000_0100; FeatureD = 0b0000_1000; // etc... } // Use features A and C auto myOptions = Options.FeatureA | Options.FeatureC; That possibility means that D *can't* check for validity as you suggest.I'm convinced that scenario *should* be considered an entirely separate thing because cramming it together with regular enumerations creates conflicting goals with the two usages of "enum", and forces unfortunate design compromises with both.[...] Yeah, I find using enums for bitfields a bit ugly. I mean, I love using bitfields too (don't know if this is because of my C/C++ background tending toward premature optimization) but conflating them with enums that supposedly should have unique values is IMO a language smell. (And don't get me started on enums being manifest constants instead of "real" enums... I still find that jarring.) T -- Marketing: the art of convincing people to pay for what they didn't need before which you can't deliver after.
Mar 27 2013
On Wednesday, 27 March 2013 at 16:52:27 UTC, Nick Sabalausky wrote:Honestly, I hate that, too. The problem is that enum is (unfortunately) intended to do double-duty as a bitfield so you can do something like this: enum Options { FeatureA = 0b0000_0001; FeatureB = 0b0000_0010; FeatureC = 0b0000_0100; FeatureD = 0b0000_1000; // etc... } // Use features A and C auto myOptions = Options.FeatureA | Options.FeatureC; That possibility means that D *can't* check for validity as you suggest.It can. myOptions is an int here, as Options would decay to its base type.
Mar 27 2013
On Wed, 27 Mar 2013 13:32:06 -0400, deadalnix <deadalnix gmail.com> wrote:On Wednesday, 27 March 2013 at 16:52:27 UTC, Nick Sabalausky wrote:No, it's not. try it. I thought as you did too until recently. And before you go checking, it's not a bug, it's functioning per the spec. -SteveHonestly, I hate that, too. The problem is that enum is (unfortunately) intended to do double-duty as a bitfield so you can do something like this: enum Options { FeatureA = 0b0000_0001; FeatureB = 0b0000_0010; FeatureC = 0b0000_0100; FeatureD = 0b0000_1000; // etc... } // Use features A and C auto myOptions = Options.FeatureA | Options.FeatureC; That possibility means that D *can't* check for validity as you suggest.It can. myOptions is an int here, as Options would decay to its base type.
Mar 27 2013
On Wednesday, 27 March 2013 at 17:36:46 UTC, Steven Schveighoffer wrote:On Wed, 27 Mar 2013 13:32:06 -0400, deadalnix <deadalnix gmail.com> wrote:I knew the bug existed, but I thought it would be solved now. If it is how the spec specify it, then it is a spec bug and a compiler bug.On Wednesday, 27 March 2013 at 16:52:27 UTC, Nick Sabalausky wrote:No, it's not. try it. I thought as you did too until recently.Honestly, I hate that, too. The problem is that enum is (unfortunately) intended to do double-duty as a bitfield so you can do something like this: enum Options { FeatureA = 0b0000_0001; FeatureB = 0b0000_0010; FeatureC = 0b0000_0100; FeatureD = 0b0000_1000; // etc... } // Use features A and C auto myOptions = Options.FeatureA | Options.FeatureC; That possibility means that D *can't* check for validity as you suggest.It can. myOptions is an int here, as Options would decay to its base type.
Mar 27 2013
On Wed, 27 Mar 2013 18:32:06 +0100 "deadalnix" <deadalnix gmail.com> wrote:On Wednesday, 27 March 2013 at 16:52:27 UTC, Nick Sabalausky wrote:Options myOptions = Options.FeatureA | Options.FeatureC;Honestly, I hate that, too. The problem is that enum is (unfortunately) intended to do double-duty as a bitfield so you can do something like this: enum Options { FeatureA = 0b0000_0001; FeatureB = 0b0000_0010; FeatureC = 0b0000_0100; FeatureD = 0b0000_1000; // etc... } // Use features A and C auto myOptions = Options.FeatureA | Options.FeatureC; That possibility means that D *can't* check for validity as you suggest.It can. myOptions is an int here, as Options would decay to its base type.
Mar 27 2013
Nick Sabalausky:enum Options { FeatureA = 0b0000_0001; FeatureB = 0b0000_0010; FeatureC = 0b0000_0100; FeatureD = 0b0000_1000; // etc... } // Use features A and C auto myOptions = Options.FeatureA | Options.FeatureC; That possibility means that D *can't* check for validity as you suggest. I'm convinced that scenario *should* be considered an entirely separate thing because cramming it together with regular enumerations creates conflicting goals with the two usages of "enum", and forces unfortunate design compromises with both.This was discussed in past. A library code BitFlags similar to struct bitfields is probably able to solve most of this problem in a mostly type safe way. If you want a built-in solution, with a bitflags, you will have to wait longer. Bye, bearophile
Mar 27 2013
On Wed, Mar 27, 2013 at 04:34:19PM +0100, Vidar Wahlberg wrote: [...]- While the "auto"-keyword often is great, it can lead to difficulties, especially when used as the return type of a function, such as "auto foo() { return bar; }". Sometimes you may wish to store the result of a function/method call as a global variable/class member, but when the function/method returns "auto" it's not apparent what the data type may be.This is true. Sometimes you really *do* want to know what the return type of an auto function is. However, you can work around this by using typeof: auto func() { return mysteriousType(); } void main() { alias RetType = typeof(func()); // Look, ma! I can use a type without knowing what it // is! RetType t = func(); ... }While you may be able to find out what "bar" is by digging in the source code, it can still be difficult to find.I think this is a bad solution. A library user shouldn't need to look at library source code to figure out what a return type is.One example is to save the result of "std.regex.match()" as a member in a class. For me the solution was to "import std.traits", create a function "auto matchText(string text) { return match(text, myRegex); }" and define the class member as "ReturnType!matchText matchResult;" (do also note that function & member must come in the right order for this to compile). This was all but obvious to a novice D coder as myself, the solution was suggested to me in the IRC channel.I find this solution a bit cumbersome. D already has a built-in typeof operator, there's no need to write tons of wrappers everywhere just to get a return type out. I would write it like this: class C { // Capture the return type of std.regex.match alias MatchResult = typeof(std.regex.match("", "")); // Now you can store it MatchResult result; this(string target, string regex) { result = std.regex.match(target, regex); } }Gotchas: -------- - The lack of "rectangular" arrays created at runtime in D ("int i = 5; int[i][i] foo;") can be quite confusing for programmers with Java or C++ background. Even though there exists alternatives (http://denis-sh.github.com/phobos-additions/unstd.multidimensionalarray.html), this design decision and how to get around it when you really desire a "rectangular" array could be explained in more detail at http://dlang.org/arrays.html.We really need rectangular arrays in Phobos. Denis has written one, as you linked above, and I've written one, too (which is similar to Denis' version). But this is such a common usage that we really should have a library module for it. Note, however, that if all but 1 of the dimensions are known, then you *can* have rectangular arrays: // This is a rectangular array int[3][4][5] rect; See also: http://wiki.dlang.org/Dense_multidimensional_arrays- Static array versus dynamic array was one of the first traps I stepped on (http://forum.dlang.org/thread/jnu1an$rjr$1 digitalmars.com). Until Jonathan M. Davis explained it in detail (http://d.puremagic.com/issues/show_bug.cgi?id=8026#c4), I pretty much considered it as "magic" that "randomShuffle(staticArray);" did not sort the array while "randomShuffle(staticArray[]);" did (the first call now gives you an compile error, though). That static arrays are value types while dynamic arrays are reference types may not be obvious for those with primarily Java background.Well, the documentation needs to be improved in this regard, that's for sure. But language-wise, there's really nothing wrong with it (it's not a sin to be different from Java).- When casting a value to an enum, there's no checking that the value actually is a valid enum value. Don't think I ever found a solution on how to check whether the value after casting is a valid enum value, it hasn't been a pressing issue.Yeah, I find this to be questionable behaviour too. But then again, in D, one is expected to use std.conv for conversions (casting is generally not recommended unless there's no other way to do it), and std.conv.conv does check for valid enum values: enum A { abc = 100, def = 200 } A a; writeln(a); // prints "abc" a = to!A(150); // this throws a runtime exception: 150 is not // in the enum.- Compiling where DMD can't find all modules cause a rather cryptic error message. A solution is to make sure you specify all source files when compiling.You could use rdmd, which I believe automatically scans for all source files you depend on.Wishlist: --------- - "void[T]" associative array (i.e. a "set") would be nice, can be achieved with "byte[0][T]".I think we should make a Phobos module for this. I, for one, found myself needing such a type.- "Foo foo = new Foo();" for global variables/class members. Now you must "Foo foo; static this() {foo = new Foo();}".Yeah, this is an irksome limitation. Though, it does have the benefit of not letting you do something that incurs runtime-cost without being explicit about it. Still, it is cumbersome to have to use static this() all over the place. It would be nice for the compiler to automatically lower such things into implicit ctor calls. T -- A bend in the road is not the end of the road unless you fail to make the turn. -- Brian White
Mar 27 2013
On Wed, 27 Mar 2013 10:05:08 -0700 "H. S. Teoh" <hsteoh quickfur.ath.cx> wrote:On Wed, Mar 27, 2013 at 04:34:19PM +0100, Vidar Wahlberg wrote: [...] =20I wouldn't want such things to be implicitly converted to static ctors unless the cyclic dependency issue was somehow ironed out. I don't mind initializers of mutable vars being implicitly run at runtime (that convenience would certainly be nice), but I definitely don't want "cyclic dependency error" being triggered by something so innocent-looking.- "Foo foo =3D new Foo();" for global variables/class members. Now you must "Foo foo; static this() {=A0foo =3D new Foo();=A0}".=20 Yeah, this is an irksome limitation. Though, it does have the benefit of not letting you do something that incurs runtime-cost without being explicit about it. Still, it is cumbersome to have to use static this() all over the place. It would be nice for the compiler to automatically lower such things into implicit ctor calls. =20
Mar 27 2013
On Wed, Mar 27, 2013 at 01:59:13PM -0400, Nick Sabalausky wrote:On Wed, 27 Mar 2013 10:05:08 -0700 "H. S. Teoh" <hsteoh quickfur.ath.cx> wrote:True.On Wed, Mar 27, 2013 at 04:34:19PM +0100, Vidar Wahlberg wrote: [...]I wouldn't want such things to be implicitly converted to static ctors unless the cyclic dependency issue was somehow ironed out.- "Foo foo = new Foo();" for global variables/class members. Now you must "Foo foo; static this() { foo = new Foo(); }".Yeah, this is an irksome limitation. Though, it does have the benefit of not letting you do something that incurs runtime-cost without being explicit about it. Still, it is cumbersome to have to use static this() all over the place. It would be nice for the compiler to automatically lower such things into implicit ctor calls.I don't mind initializers of mutable vars being implicitly run at runtime (that convenience would certainly be nice), but I definitely don't want "cyclic dependency error" being triggered by something so innocent-looking.I wonder if it would solve the problem if different instances of static ctors in the same module are regarded as separate entities with their own dependencies, so that you won't get a cyclic dependency error unless there's truly an irreducible cyclic dependency. That is, cyclic dependency errors shouldn't happen just because *one* static ctor depends on stuff that indirectly depends on one of the 20 other static ctors. Don't know if this will cause conflicts with the current way module dependencies work, though. T -- Кто везде - тот нигде.
Mar 27 2013
On Wed, 27 Mar 2013 11:34:19 -0400, Vidar Wahlberg <vidar.wahlberg gmail.com> wrote:- When casting a value to an enum, there's no checking that the value actually is a valid enum value. Don't think I ever found a solution on how to check whether the value after casting is a valid enum value, it hasn't been a pressing issue.Typically, one uses std.conv.to to safely convert one value into another. Cast should be avoided unless absolutely necessary. I just tested it, it works on enum *strings*, but not enum *values* For example: import std.conv; enum X { i, j, k } void main() { X x = to!X("i"); // works! x = to!X(1); // fails! } I think to should be able to do this, but I'm not a good enough guru with templates and compile-time type info to know if it's possible. Anyone know if this is possible? If so, I think it should be added. -Steve
Mar 27 2013
On Wed, 27 Mar 2013 13:08:19 -0400I just tested it, it works on enum *strings*, but not enum *values* For example: import std.conv; enum X { i, j, k } void main() { X x = to!X("i"); // works! x = to!X(1); // fails! }Works for me on 2.063 Win. Keep in mind: assert(cast(int)X.i == 0); assert(cast(int)X.j == 1);
Mar 27 2013
On Wed, 27 Mar 2013 14:07:27 -0400 Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> wrote:On Wed, 27 Mar 2013 13:08:19 -0400I meant of course 2.062I just tested it, it works on enum *strings*, but not enum *values* For example: import std.conv; enum X { i, j, k } void main() { X x = to!X("i"); // works! x = to!X(1); // fails! }Works for me on 2.063 Win. Keep in mind: assert(cast(int)X.i == 0); assert(cast(int)X.j == 1);
Mar 27 2013
On Wed, 27 Mar 2013 14:09:07 -0400, Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> wrote:On Wed, 27 Mar 2013 14:07:27 -0400 Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> wrote:Hah, I have not yet downloaded 2.062. It did not work in 2.061, not sure if that was a bug or it's a new feature. anyway, that is good to know! -SteveOn Wed, 27 Mar 2013 13:08:19 -0400I meant of course 2.062I just tested it, it works on enum *strings*, but not enum *values* For example: import std.conv; enum X { i, j, k } void main() { X x = to!X("i"); // works! x = to!X(1); // fails! }Works for me on 2.063 Win. Keep in mind: assert(cast(int)X.i == 0); assert(cast(int)X.j == 1);
Mar 27 2013
On 03/27/2013 03:47 PM, Steven Schveighoffer wrote:On Wed, 27 Mar 2013 14:09:07 -0400, Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> wrote:It worked for me in 2.061, as that's what I'm currently on. Waiting to build the release that I hear might have shared objects!On Wed, 27 Mar 2013 14:07:27 -0400 Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> wrote:Hah, I have not yet downloaded 2.062. It did not work in 2.061, not sure if that was a bug or it's a new feature. anyway, that is good to know! -SteveOn Wed, 27 Mar 2013 13:08:19 -0400I meant of course 2.062I just tested it, it works on enum *strings*, but not enum *values* For example: import std.conv; enum X { i, j, k } void main() { X x = to!X("i"); // works! x = to!X(1); // fails! }Works for me on 2.063 Win. Keep in mind: assert(cast(int)X.i == 0); assert(cast(int)X.j == 1);
Mar 28 2013
On 03/27/2013 12:08 PM, Steven Schveighoffer wrote:On Wed, 27 Mar 2013 11:34:19 -0400, Vidar Wahlberg <vidar.wahlberg gmail.com> wrote:- When casting a value to an enum, there's no checking that the value actually is a valid enum value. Don't think I ever found a solution on how to check whether the value after casting is a valid enum value, it hasn't been a pressing issue.Typically, one uses std.conv.to to safely convert one value into another. Cast should be avoided unless absolutely necessary. I just tested it, it works on enum *strings*, but not enum *values* For example: import std.conv; enum X { i, j, k } void main() { X x = to!X("i"); // works! x = to!X(1); // fails! }I think to should be able to do this, but I'm not a good enough guru with templates and compile-time type info to know if it's possible. Anyone know if this is possible? If so, I think it should be added. -SteveIt also works on values.... enum A { B, C, D, } int w; import std.conv; import std.stdio; void main() { auto t = w.to!A.B; writeln(t); }
Mar 28 2013
On Wednesday, 27 March 2013 at 15:34:20 UTC, Vidar Wahlberg wrote:I know I'm probably going to upset some people with this, bashing their favourite child and all, but I wanted to let you know the experience I've had with D so far, as a novice D coder with a heavy Java & light C++ background.I think D has quite a strong set of critic users, namely because the benefits are just too great. Not to suggest less importance to these issues, but these are known issues. It is good to have them brought up from the new user perspective.Woes: ----- - I find myself in a world of pain when I want to share dataI don't use this, but my understanding is that 'shared' is a low level feature. In general one doing threading should never touch it and instead a library is built from it. The problem is we don't have a good threading library, we have parallelism and concurrency which may use threads but not always what one would expect when looking for threading. I could be completely wrong here!- While the "auto"-keyword often is great, it can lead to difficulties, especially when used as the return type of a function, such as "auto foo() { return bar; }". Sometimes you may wish to store the result of a function/method call as a global variable/class member, but when the function/method returns "auto" it's not apparent what the data type may be.My observation is that many times in D the type is something you probably don't want to write out even if you know it. Using typeof() tends to be a better choice even if it isn't straight forward.Gotchas: -------- - The lack of "rectangular" arrays created at runtime in D ("int i = 5; int[i][i] foo;") can be quite confusing for programmers with Java or C++ background.Well in this case you are trying to create a static 2D array with a runtime value. I haven't had issues with 2D arrays, but I'm not familiar with "rectangular" arrays.- Static array versus dynamic array was one of the first traps I stepped onStatic arrays are very hidden. Much like arrays are hidden in C.- When casting a value to an enum, there's no checking that the value actually is a valid enum value. Don't think I ever found a solution on how to check whether the value after casting is a valid enum value, it hasn't been a pressing issue.This would be nice, but enum's are also nice for binary flags. auto option = My.A | My.B | My.C; option would be a valid My for how it is used, but it is not defined as valid. There has been discussions on alternatives to using an enum (but enum is convenient).- Compiling where DMD can't find all modules cause a rather cryptic error message. A solution is to make sure you specify all source files when compiling.The compile => link process is pretty well hidden in most languages these days (many don't have it).Wishlist: --------- - "void[T]" associative array (i.e. a "set") would be nice, can be achieved with "byte[0][T]".I think a proper library container would be fine.- "Foo foo = new Foo();" for global variables/class members. Now you must "Foo foo; static this() { foo = new Foo(); }".This won't happen. It is kind of a feature. Otherwise you'd be confused with the static this dependency error because "I'm not using static this."
Mar 27 2013
27-Mar-2013 19:34, Vidar Wahlberg пишет:I know I'm probably going to upset some people with this, bashing their favourite child and all, but I wanted to let you know the experience I've had with D so far, as a novice D coder with a heavy Java & light C++ background. It's not that I dislike D, in fact there are tons of things I love about it, it's pretty much exactly what I'm looking for in a programming language at the moment. Yet, I encounter some frustrating issues when coding, often leaving me with the impression that I'm fighting the language more than the problem I'm trying to solve. True, there are many things I don't know about D, compilers or the inner workings of a computer, and some of the fights I have with the language are likely started by myself because I'm dragging along my bias from other languages, drawing misconceptions on how the D language actually works. My intentions are not to insult, but shed some light on some of the difficulties I've faced (and how I solved them), with the hope that it will help others from facing the same difficulties. Woes: ----- - I find myself in a world of pain when I want to share data more complex than the basic data types (int, char, byte, etc) across threads. Seemingly the magic trick is to "cast(shared) foo" (or "cast(immutable)") when passing objects/references to another thread, then "cast(Foo)" back on the receiving end (as most classes/structs in the standard library refuse to let you call any methods when the object is shared). The examples in the source and TDPL are fairly limited on the issue, it mostly covers only those basic data types. - While the "auto"-keyword often is great, it can lead to difficulties, especially when used as the return type of a function, such as "auto foo() { return bar; }". Sometimes you may wish to store the result of a function/method call as a global variable/class member, but when the function/method returns "auto" it's not apparent what the data type may be. While you may be able to find out what "bar" is by digging in the source code, it can still be difficult to find. One example is to save the result of "std.regex.match()" as a member in a class. For me the solution was to "import std.traits", create a function "auto matchText(string text) { return match(text, myRegex); }" and define the class member as "ReturnType!matchText matchResult;" (do also note that function & member must come in the right order for this to compile).Currently documentation for match states in plain text that: " ... Returns: a RegexMatch object holding engine state after first match. " Where RegexMatch is a template to be found in the the same module. I can see that docs are nothing stellar but the key info is present. That being said I see no problem with ReturnType!xyz, typeof(smth) or just typing auto.This was all but obvious to a novice D coder as myself, the solution was suggested to me in the IRC channel. Gotchas: -------- - The lack of "rectangular" arrays created at runtime in D ("int i = 5; int[i][i] foo;") can be quite confusing for programmers with Java or C++ background.The code you suggested doesn't work in both C and Java.Even though there exists alternatives (http://denis-sh.github.com/phobos-additions/unstd.multidimensionalarray.html), this design decision and how to get around it when you really desire a "rectangular" array could be explained in more detail at http://dlang.org/arrays.html.IRC you can get jagged arrays with the syntax: int[][] arr = new int[][](x,y);- Compiling where DMD can't find all modules cause a rather cryptic error message. A solution is to make sure you specify all source files when compiling.Linker... there are many ways to improve on that old technology that but not much have been done. And the problem is not D specific. There is a tool rdmd that tracks all dependencies: rdmd main_module.d That plus: rdmd --build-only main_module.d Is more then enough in 90% of cases for me.Wishlist: --------- - "void[T]" associative array (i.e. a "set") would be nice, can be achieved with "byte[0][T]".We need a better set anyway as hash-map is a good map but suboptimal as a set. -- Dmitry Olshansky
Mar 27 2013
On 3/27/13 11:34 AM, Vidar Wahlberg wrote:I know I'm probably going to upset some people with this, bashing their favourite child and all, but I wanted to let you know the experience I've had with D so far, as a novice D coder with a heavy Java & light C++ background.[snip] We're very interested (and therefore grateful) in hearing experience reports. There have been a number of responses already so I'll just insert a couple of brief points. * The MSB I'm seeing for all of these grievances is that we must improve our documentation. * We used to have reference semantics for static arrays, it was a nightmare. * As mentioned, "shared" is a last-resort, intentionally limited of communicating between threads. We must improve our message passing infrastructure (including documentation and examples). Definitely we must finish all details of what shared means (and doesn't). This remains largely an educational issue because people coming from share-intensive language expect to use shared casually (much as seasoned C++ users expected to use const casually; this particular issue has been largely resolved). * We need to have a battery of multidimensional array shapes along with simple iteration and access primitives, at least for interfacing with scientific libraries that define and expect such formats. I'm thinking rectangular (generally hyperrectangular) matrices, triangular matrices, sparse matrices, and band matrices. Thanks again for sharing your thoughts. Andrei
Mar 27 2013
On Wed, Mar 27, 2013 at 04:22:00PM -0400, Andrei Alexandrescu wrote: [...]* We need to have a battery of multidimensional array shapes along with simple iteration and access primitives, at least for interfacing with scientific libraries that define and expect such formats. I'm thinking rectangular (generally hyperrectangular) matrices, triangular matrices, sparse matrices, and band matrices.[...] Given that different computational needs will require different implementations, it might be a good idea to define a generic set of templates for identifying something as a multi-dimensional array, and adopt some conventions as to how things are named (e.g., .dimensions[i] to retrieve the length of the i'th dimension of the array, or the fact that .opIndex(a,b,c,...) is defined, etc.). The idea is that we want to implement some generic array algorithms that don't care about whether it's a dense array, triangular matrix, sparse array, etc.. These should be independent of the actual storage format. There are, of course, algorithms that will benefit from specific implementations (e.g. algorithms that take advantage of the sparseness of a matrix, say), so those will specificially depend on, say, a sparse array. Generic identification of array types is also necessary to maximize interoperability between computational libraries. For one thing, I do *not* wish to see a repeat of the C++ situation where the multidimensional array types between different libraries are incompatible and require expensive copying and/or layers upon layers of wrappers just to interoperate. The situation in D should be such that any library's array type should be interoperable with any other library's array type, as long as both satisfy the standard array-identification templates. In a nutshell, multidimensional arrays should have a standard API so that any multidimensional array can be used with any algorithm that expects one. T -- Always remember that you are unique. Just like everybody else. -- despair.com
Mar 27 2013
On 2013-03-27 16:34, Vidar Wahlberg wrote:- I find myself in a world of pain when I want to share data more complex than the basic data types (int, char, byte, etc) across threads. Seemingly the magic trick is to "cast(shared) foo" (or "cast(immutable)") when passing objects/references to another thread, then "cast(Foo)" back on the receiving end (as most classes/structs in the standard library refuse to let you call any methods when the object is shared). The examples in the source and TDPL are fairly limited on the issue, it mostly covers only those basic data types.Try message passing and serialize the data you want to send. Message passing: http://dlang.org/phobos/std_concurrency.html Serialization: https://github.com/jacob-carlborg/orange -- /Jacob Carlborg
Mar 28 2013