Re: Rant after trying Rust a bit
On Thu, Jul 23, 2015 at 01:40:17PM -0700, Walter Bright via Digitalmars-d wrote: On 7/23/2015 12:50 PM, H. S. Teoh via Digitalmars-d wrote: That assumes the template author is diligent (foolhardy?) enough to write unittests that cover all possible instantiations... No, only each branch of the template code must be instantiated, not every possible instantiation. And we have a tool to help with that: -cov Does anyone believe it is a good practice to ship template code that has never been instantiated? OK, I jumped into the middle of this discussion so probably I'm speaking totally out of context... but anyway, with regards to template code, I agree that it ought to be thoroughly tested by at least instantiating the most typical use cases (as well as some not-so-typical use cases). An uninstantiated template path is worse than a branch that's never taken, because the compiler can't help you find obvious problems before you ship it to the customer. A lot of Phobos bugs lurk in rarely-used template branches that are not covered by the unittests. Instantiating all branches is only part of the solution, though. A lot of Phobos bugs also arise from undetected dependencies of the template code on the specifics of the concrete types used to test it in the unittests. The template passes the unittest but when you instantiate it with a type not used in the unittests, it breaks. For instance, a lot of range-based templates are tested with arrays in the unittests. Some of these templates wrongly depend on array behaviour (as opposed to being confined only to range API operations) while their signature constraints indicate only the generic range API. As a result, when non-array ranges are used, it breaks. Sometimes bugs like this can lurk undetected for a long time before somebody one day happens to instantiate it with a range type that violates the hidden assumption in the template code. If we had a Concepts-like construct in D, where template code is statically constrained to only use, e.g., range API when manipulating an incoming type, a lot of these bugs would've been caught. In fact, I'd argue that this should be done for *all* templates -- for example, a function like this ought to be statically rejected: auto myFunc(T)(T t) { return t + 1; } because it assumes the validity of the + operation on T, but T is not constrained in any way, so it can be *any* type, most of which, arguably, do not support the + operation. Instead, templates ought to be required to explicitly declare up-front all operations that it will perform on incoming types, so that (1) its assumptions are obvious, and (2) the compiler will reject attempts to instantiate it with an incompatible type. auto myFunc(T)(T t) if (is(typeof(T.init + 1))) { return t + 1; } The current syntax is ugly, of course, but that's easily remedied. The more fundamental problem is that the compiler does not restrict operations on T in any way, even when the sig constraint specifies how T ought to be used. Someone could easily introduce a bug: auto myFunc(T)(T t) if (is(typeof(T.init + 1))) { /* Oops, we checked that +1 is a valid operation on T, * but here we're doing -1 instead, which may or may not * be valid: */ return t - 1; } The compiler still accepts this code as long as the unittests use types that support both + and -. So this dependency on the incidental characteristics of T remains as a latent bug. If the compiler outright rejected any operation on T that hasn't been explicitly tested for, *then* we will have eliminated a whole class of template bugs. Wrong code like the last example above would be caught as soon as the compiler compiles the body of myFunc. T -- Elegant or ugly code as well as fine or rude sentences have something in common: they don't depend on the language. -- Luca De Vitis
[Issue 14765] [Reg2.068.0] Rangified functions no longer accept types that implicitly cast to string
https://issues.dlang.org/show_bug.cgi?id=14765 --- Comment #5 from github-bugzi...@puremagic.com --- Commits pushed to stable at https://github.com/D-Programming-Language/phobos https://github.com/D-Programming-Language/phobos/commit/31c963070245c2c89ae287283f49fc77425c45df add std.traits.isAutodecodableString() to fix Issue 14765 https://github.com/D-Programming-Language/phobos/commit/39806a85f5cd0f3280519277a5943e3485abd797 Merge pull request #3512 from MartinNowak/fix14765 add std.traits.isAutodecodableString() to fix Issue 14765 --
[Issue 14765] [Reg2.068.0] Rangified functions no longer accept types that implicitly cast to string
https://issues.dlang.org/show_bug.cgi?id=14765 github-bugzi...@puremagic.com changed: What|Removed |Added Status|NEW |RESOLVED Resolution|--- |FIXED --
Re: dmd 2.068, 2.069, 2.0xx Evil Plan going forward
On Tue, 21 Jul 2015 01:54:44 -0700, Walter Bright wrote: On 7/20/2015 11:34 PM, Martin Nowak wrote: I got this number from Daniel, he didn't found a reason. Chances are it's uniformly slower because of dmd's backend, but of course profiling might help. Consider that the Win32 version of dmd is built with with the same backend as dmd. If there's a slowdown with that version, it isn't due to the backend. DMC version of DMD is noticably slower on building phobos than MinGW version. i'm using HEAD built with MinGW with wine, and the difference can be noticed with my eyes. i didn't measure that with time, though. so backend is surely plays a role here. signature.asc Description: PGP signature
Re: Rant after trying Rust a bit
On 7/23/2015 1:08 PM, Dicebot wrote: I am not sure how it applies. D interfaces (defined with the 'interface' keyword) are simple dispatch types, they don't require an Object. Such interfaces can also have default implementations. My point was about the fact that `isInputRange` and `InputRangeObject` are the same entities in Rust, simply interpreted differently by compiler depending on usage context. I understand. This is important because you normally want to design your application in terms of template constraints and structs to get most out of inlining and optimization. However, to define stable ABI for shared libraries, the very same interfaces need to be wrapped in runtime polymorphism. Closest thing in D would be to define traits as interfaces and use code like this: void foo(T)() if ( (is(T == struct) || is(T == class)) Matches!(T, Interface) ) { } where `Matches` is a template helper that statically iterates method list of interface and looks for matching methods in T. I don't think the test for struct and class is necessary. It can be just: void foo(T)() if (Matches!(T, Interface)) { ... } as opposed to: void foo(T : Interface)() { ... } However, making it built-in feels really convenient in Rust: - considerably less function declaration visual noise It's less noise, sure, and perhaps we can do some syntactical sugar to improve that. But I don't think this is a fundamental shortcoming for D as it stands now (although nobody has written a Matches template, perhaps that should be a priority). - much better error messages: trying to use methods of T not defined by a trait will result in compile-time error even without instantiating the template The error messages will occur at compile time and will be the same if you write a unit test to instantiate the template. As I wrote earlier, I don't really understand the need to ship template source code that has never been instantiated.
Re: Rant after trying Rust a bit
On Thursday, 23 July 2015 at 20:52:46 UTC, Walter Bright wrote: My point was about the fact that `isInputRange` and `InputRangeObject` are the same entities in Rust, simply interpreted differently by compiler depending on usage context. I understand. Ok, sorry, it wasn't clear from the response context :) I don't think the test for struct and class is necessary. It can be just: void foo(T)() if (Matches!(T, Interface)) { ... } as opposed to: void foo(T : Interface)() { ... } Correct indeed, though I don't feel it is much of a difference considering how common such code is (remember that you endorse ranges as The Way for designing APIs!) - much better error messages: trying to use methods of T not defined by a trait will result in compile-time error even without instantiating the template The error messages will occur at compile time and will be the same if you write a unit test to instantiate the template. As I wrote earlier, I don't really understand the need to ship template source code that has never been instantiated. 1) It does not protect from errors in definition void foo (R) (Range r) if (isInputRange!Range) { r.save(); } unittest { SomeForwardRange r; foo(r); } This will compile and show 100% test coverage. Yet when user will try using it with real input range, it will fail. 2) There is quite a notable difference in clarity between error message coming from some arcane part of function body and referring to wrong usage (or even totally misleading because of UFCS) and simple and straightforward Your type X does not implement method X necessary for trait Y 3) Coverage does not work with conditional compilation: void foo (T) () { import std.stdio; static if (is(T == int)) writeln(1); else writeln(2); } unittest { foo!int(); } $ dmd -cov=100 -unittest -main ./sample.d
[Issue 14822] New: DMD crash with CTFE range code (v2.067.1)
https://issues.dlang.org/show_bug.cgi?id=14822 Issue ID: 14822 Summary: DMD crash with CTFE range code (v2.067.1) Product: D Version: D2 Hardware: x86_64 OS: Linux Status: NEW Severity: normal Priority: P1 Component: dmd Assignee: nob...@puremagic.com Reporter: initrd...@gmail.com Created attachment 1531 -- https://issues.dlang.org/attachment.cgi?id=1531action=edit reduced source code I've reduced this code with DustMite and attached it to the issue. Compile using `dmd -main -unittest sqlite3.d` Output: dmd -main -unittest sqlite3.d MapResult(Result(0LU, 0LU)) dmd: struct.c:929: virtual void StructDeclaration::semantic(Scope*): Assertion `type-ty != Tstruct || ((TypeStruct *)type)-sym == this' failed. fish: “dmd -main -unittest sqlite3.d” terminated by signal SIGABRT (Abort) --
Re: Bin2d (0.2.0) Rewrite
On Thursday, 23 July 2015 at 12:07:21 UTC, Rikki Cattermole wrote: On 23/07/2015 11:56 p.m., Suliman wrote: On Thursday, 23 July 2015 at 10:15:17 UTC, Rikki Cattermole wrote: So Bin2D[0] has been rewritten and adds a bunch of nice new features. - Limit generated code by: - package modifier - ``version(unittest)`` - Use enum for usage at compile time, instead of ``const(ubyte[])`` - Report with variable name to original filename Only dependency is Phobos. The rewrite adds most importantly usage of e.g. byChunk over the input files to limit the amount of memory used. [0] https://github.com/rikkimax/bin2d Cool! Could you add example of usage in readme? There is already a pretty basic example in it. Anything specific in mind? To be honest it took me a bit to realize what it was. A Command line application that produces a d modules which represent/store those files. The example is great but it could also use a bit more generic outline Bin2d MODULENAME.d=Resource_Reference FILE1_PATH [FILE2_PATH ...] I'm not sure how its usually done. Also even though its not necessary its always nice to have an example that will compile. It also adds some context. MAIN.d --- import std.stdio; import Resource_Reference; void main() { string[] FILE_LOCATIONS = outputFilesToFileSystem(); FILE_LOCATIONS.each!writeln(); } --- compile With dmd MAIN.d MODULENAME.d Actually this didn't work for me... maybe I'm misinterpreting something.
Re: Rant after trying Rust a bit
On 7/23/2015 2:08 PM, Dicebot wrote: It does not protect from errors in definition void foo (R) (Range r) if (isInputRange!Range) { r.save(); } unittest { SomeForwardRange r; foo(r); } This will compile and show 100% test coverage. Yet when user will try using it with real input range, it will fail. That is correct. Some care must be taken that the mock types used in the unit tests actually match what the constraint is, rather than being a superset of them. There is quite a notable difference in clarity between error message coming from some arcane part of function body and referring to wrong usage (or even totally misleading because of UFCS) and simple and straightforward Your type X does not implement method X necessary for trait Y I believe they are the same. method X does not exist for type Y. Coverage does not work with conditional compilation: void foo (T) () { import std.stdio; static if (is(T == int)) writeln(1); else writeln(2); } unittest { foo!int(); } $ dmd -cov=100 -unittest -main ./sample.d Let's look at the actual coverage report: === |void foo (T) () |{ |import std.stdio; |static if (is(T == int)) 1|writeln(1); |else |writeln(2); |} | |unittest |{ 1|foo!int(); |} | foo.d is 100% covered I look at these all the time. It's pretty obvious that the second writeln is not being compiled in. Now, if I make a mistake in the second writeln such that it is syntactically correct yet semantically wrong, and I ship it, and it blows up when the customer actually instantiates that line of code, -- where is the advantage to me? -- How am I, the developer, better off? How does well, it looks syntactically like D code, so ship it! pass any sort of professional quality assurance?
Re: dmd 2.068, 2.069, 2.0xx Evil Plan going forward
On Wed, 22 Jul 2015 12:17:21 +, Martin Nowak wrote: - The backend generates pretty bad code, we'll never be able to catch up with good optimizers. Why should we invest in an outdated backend? the one reason is that it's small and doesn't require 3rd-party libraries to build. while it's not the best backend out here, change-compile-full- rebuild cycle is very fast for DMD, and doesn't require to install gcc sources or llvm. yet i'm not sure that this reason will outweigh the translation and maintenance burden. signature.asc Description: PGP signature
Re: Rant after trying Rust a bit
On Thursday, 23 July 2015 at 14:08:23 UTC, Andrei Alexandrescu wrote: Thanks for the link, good quick read to get the overview of Rust's traits feature. It's ingenious because it integrates static and dynamic dispatch. For dynamic dispatch, traits are better than interfaces - more flexible, better informed. For static dispatch, they don't hold a candle to D's constraints. This is important because dynamic dispatch is more of a cut-and-dried matter, whereas static dispatch is where it's at. On that note, I've mentioned scala's trait, which are kind of similar and worth looking at. The thing being based on java's object model, as D's object model, it is easier to think about how this could get into D. For static dispatch I think D's template constraints are quite a lot better; they have a lot more power and offer a lot more to promise. They are an out-of-the-box solution that's a bit unwieldy because it's new enough to not yet have established idioms. In contrast, traits come from straight within the box. Certainly, but they suffer from the LISP effect. You can do everything because the structure does not constrain you in any way, while at the same time it become quickly very hard to understand, for the very same reason. I do think think the opposition between the 2, as seen in your post, or Stroustrup's allergy to static if is wrong headed. May be one can be expressed via the other ?
Re: casting away const and then mutating
On Thursday, 23 July 2015 at 18:43:03 UTC, anonymous wrote: On a GitHub pull request, Steven Schveighoffer (schveiguy), Jonathan M Davis (jmdavis), and I (aG0aep6G) have been discussing if or when it's ok to cast away const and then mutate the data: https://github.com/D-Programming-Language/phobos/pull/3501#issuecomment-124169544 I've been under the impression that it's never allowed, i.e. it's always undefined behaviour. I think Jonathan is of the same opinion. It's come up time and time again with discussions for logical const. If you cast away const, it's up to you to guarantee that the data being referenced is not mutated, and if it is mutated, it's undefined behavior. Now, if you know that the data being referenced is actually mutable and not immutable, and you know that the compiler isn't going to make any assumptions based on const which are then wrong if you mutate the variable after casting away const, then you can get away with it. But it's still undefined behavior, and if the compiler later starts doing more than it does now based on the knowledge that you can't mutate via a const reference, then your code might stop working correctly. So, if you're _really_ careful, you can get away with casting away const and mutating a variable, but you are depending on undefined behavior. Steven disagrees and thinks that there are cases where it's ok. Namely, this simple case would be ok: int x; const int *y = x; *(cast(int *)y) = 5; As I understand him, he's arguing that since the data is mutable, and since no function boundaries are crossed, compilers should not be allowed to do anything but the obvious with that code. Even if this were defined behavior, what would be the point? You have access to x. You could just mutate it directly. I don't see how it would make any sense to be attempting to mutate something via a const reference when you have access to it via a mutable reference. It's when you don't have access to it via a mutable reference that it becomes an issue - which means that you've crossed a function boundary. As far as I can tell, making the above defined behavior buys you nothing. The times when you gain something from being able to cast away const and mutate are the times when you've crossed function boundaries and you have to assume that the calling code can't see that the cast is happening and thus can't see that the data it passed it might have been mutated even though it was const. The times where being able to cast away const and mutate would be valuable are exactly the times when that would be violating the purpose of const - that the data isn't changed via a const reference. The only way to make casting away const and mutating defined behavior in general is to make it so that the compiler can't make assumptions based on const, which does tend to defeat the purpose of const on some level. And part of the whole deal with D's const is that it's actually physical const and not logical const or C++'s const or any other type of const, and if that's the case, then casting away const and mutating is _not_ something that should be defined behavior. If it were, then we wouldn't be dealing with physical const anymore. Instead we'd be in the same boat as C++ where const didn't actually mean that the object wasn't mutated, since you could cast away mutate - just with the caveat that you have to be sure that the data wasn't actually immutable, since mutating immutable data _definitely_ breaks immutable, and it could segfault, depending on where the data is stored. So, I don't see how we say that it makes sense for const to ever be cast away and then mutated. That violates the guarantees that const is supposed to provide and puts us back in the C++ boat, only worse, since you still have to worry about immutable and not mutating const when it's actually immutable. - Jonathan M Davis
Re: Rant after trying Rust a bit
On 7/23/2015 12:50 PM, H. S. Teoh via Digitalmars-d wrote: That assumes the template author is diligent (foolhardy?) enough to write unittests that cover all possible instantiations... No, only each branch of the template code must be instantiated, not every possible instantiation. And we have a tool to help with that: -cov Does anyone believe it is a good practice to ship template code that has never been instantiated?
Re: Rant after trying Rust a bit
On 7/23/2015 12:50 PM, Tobias Müller wrote: TBH I'm very surprised about that argument, because boolean conditions with version() were dimissed for exactly that reason. I knew someone would bring that up :-) No, I do not believe it is the same thing. For one thing, you cannot test the various versions on one system. On any one system, you have to take on faith that you didn't break the version blocks on other systems. This is quite unlike D's template constraints, where all the combinations can be tested reliably with a unittest{} block.
Re: Rant after trying Rust a bit
On Wed, Jul 22, 2015 at 4:25 PM, jmh530 via Digitalmars-d digitalmars-d@puremagic.com wrote: I feel like it's hard to separate borrowing from Rust's variety of pointers ( is borrowed pointer, ~ is for unique pointer, @ is for managed pointer). Rust has ditched the ~ and @ syntax for pointers for a long time now. For unique pointers they use BoxT, and for managed pointers it is either RcT, ArcT, or GcT, depending on the desired behavior. The last one is currently still under development I believe.
Re: Rant after trying Rust a bit
On Thursday, 23 July 2015 at 20:40:17 UTC, Walter Bright wrote: On 7/23/2015 12:50 PM, H. S. Teoh via Digitalmars-d wrote: That assumes the template author is diligent (foolhardy?) enough to write unittests that cover all possible instantiations... No, only each branch of the template code must be instantiated, not every possible instantiation. And we have a tool to help with that: -cov Does anyone believe it is a good practice to ship template code that has never been instantiated? I dunno about good practices but I have some use cases. I write a bunch of zero-parameter template methods and then pass them into a Match template which attempts to instantiate each of them in turn, settling on the first one which does compile. So the methods basically form a list of preferred implementation of functionality X. All but one winds up uninstantiated. I also use a pattern where I mix in a zero-parameter template methods into a struct - they don't necessarily work for that struct, but they won't stop compilation unless they are instantiated. A complete interface is generated but only the subset which the context actually supports can be successfully instantiated - and anything the caller doesn't need, doesn't get compiled. Again, not sure if this is a bad or good thing. But I have found these patterns useful.
Re: Overloading Based on Constraints
On 7/23/15 5:58 PM, jmh530 wrote: I was looking at http://dlang.org/concepts.html where it discusses overloading templates based on constraints. I wanted to try to overload a template function with another version that does everything in place. So basically, one has a return and the other doesn't. However, when I run the code, it only calls the first function. The second one is ignored. I tried a number of adjustments, but the only thing that worked is to re-name the function something else, remove the U's, and just have it be a void function. Just so you know, *every* template is tried for every call. So the if statements are not dependent on each other. Something I've wanted for a long time is an if-else mechanism for template constraints. What ends up happening is you have to repeat your constraints on every overload, but negate the previous ones. In any case, to your code: import std.stdio : writeln; import std.traits; T test(T)(T x) if (isNumeric!(T)) { writeln(calling test without void); T y = x; y += 1; return y; } U test(T, U)(ref T x) if (isNumeric!(T) is(U == void)) this will NEVER be called via IFTI, because U cannot be determined from the parameters. Return type (or how you use the result) does not play into IFTI at all. This is likely why it's never used, because it fails to instantiate. And note that regular overloading does not work like this. You can't overload on return type. Overloading on ref works, but only makes it so you can dictate how to handle rvalues vs. lvalues differently. -Steve
Re: Bin2d (0.2.0) Rewrite
On Thursday, 23 July 2015 at 10:15:17 UTC, Rikki Cattermole wrote: So Bin2D[0] has been rewritten and adds a bunch of nice new features. - Limit generated code by: - package modifier - ``version(unittest)`` - Use enum for usage at compile time, instead of ``const(ubyte[])`` - Report with variable name to original filename Only dependency is Phobos. The rewrite adds most importantly usage of e.g. byChunk over the input files to limit the amount of memory used. [0] https://github.com/rikkimax/bin2d Cool! Could you add example of usage in readme?
Re: Converting uint[] slice to string for the purpose of hashing?
On Thursday, 23 July 2015 at 11:49:05 UTC, cym13 wrote: On Thursday, 23 July 2015 at 11:15:46 UTC, Enjoys Math wrote: 1. Is the best way to hash a uint[] slice 2. How do you do it? IIRC, std.digest functions take ubyte[] as input, so to hash a uint[] I would do the following: void main(string[] args) { import std.conv; import std.digest.md; int[] a = [1, 2, 3, 4, 5]; auto md5 = new MD5Digest(); md5.put(a.to!(ubyte[])); auto hash = md5.finish(); writeln(hash); } Thanks. That worked. Here's my code: module hashtools; import std.conv; import std.digest.md; string uintSliceToHash(const uint[] slice) { auto md5 = new MD5Digest(); md5.put(slice.to!(ubyte[])); return md5.finish().to!(string); } unittest { import std.stdio; uint[] slice = [1,2,3,4]; writeln(uintSliceToHash(slice)); }
Re: Converting uint[] slice to string for the purpose of hashing?
On Thursday, 23 July 2015 at 12:10:04 UTC, Enjoys Math wrote: On Thursday, 23 July 2015 at 11:49:05 UTC, cym13 wrote: [...] Thanks. That worked. Here's my code: module hashtools; import std.conv; import std.digest.md; string uintSliceToHash(const uint[] slice) { auto md5 = new MD5Digest(); md5.put(slice.to!(ubyte[])); return md5.finish().to!(string); } unittest { import std.stdio; uint[] slice = [1,2,3,4]; writeln(uintSliceToHash(slice)); } Actually, uint[] seems to be hashable: import std.stdio; int[uint[]] aa; aa[[1,2,3]] = 5; writeln(aa[[1,2,3]]); WORKS
Converting uint[] slice to string for the purpose of hashing?
1. Is the best way to hash a uint[] slice 2. How do you do it?
Re: Rant after trying Rust a bit
On Wednesday, 22 July 2015 at 21:04:57 UTC, simendsjo wrote: :) The example was written to save space. I recon you understand what I mean. Yeah, but the if/else is one of the most useful examples of it, and is covered by ?:, so the whole thing becomes less compelling then. The other places where I've used it in languages that support it are little blocks crammed into a line and sometimes exception grabbing... but still, the value isn't that great.
Re: Rant after trying Rust a bit
On 7/22/15 7:47 PM, rsw0x wrote: On Wednesday, 22 July 2015 at 18:47:33 UTC, simendsjo wrote: ... I think rust makes the ugliness of D's push everything into phobos for simplicity become very visible. D and Rust share many equal constructs, but D's is almost always uglier. Care for a list? Thanks! -- Andrei
Re: Converting uint[] slice to string for the purpose of hashing?
On Thursday, 23 July 2015 at 11:15:46 UTC, Enjoys Math wrote: 1. Is the best way to hash a uint[] slice 2. How do you do it? IIRC, std.digest functions take ubyte[] as input, so to hash a uint[] I would do the following: void main(string[] args) { import std.conv; import std.digest.md; int[] a = [1, 2, 3, 4, 5]; auto md5 = new MD5Digest(); md5.put(a.to!(ubyte[])); auto hash = md5.finish(); writeln(hash); }
Re: Rant after trying Rust a bit
On 7/22/15 5:36 PM, jmh530 wrote: On Wednesday, 22 July 2015 at 20:43:04 UTC, simendsjo wrote: When everything is an expressions, you can write things like auto a = if(e) c else d; In D you have to write type a = invalid_value; if(e) a = c; else a = d; assert(a != invalid_value); I prefer this example from one of the various Rust tutorials let foo = if x == 5 { five } else if x == 6 { six } else { neither } You're basically using a conditional expression as an rvalue. You can do the same thing with a { } block. I used to be quite jazzed about the everything-is-an-expression mantra, but it's not all great. 1. Inferring function return types when everything is an expression (i.e. last expression there is the return type) may yield WAT results. 2. Defining a result type for loops is awkward. At the end of the day everything-is-an-expression is natural for functional languages, but doesn't seem it makes a large difference to an imperative language. To OP: thanks for your rant! Instead of getting defensive we'd do good to derive action items from it. Andrei
Silicon Valley D Meetup July 2015
Thanks to Chris Jacobi, we may have a permanent venue: Hacker Dojo in Mountain View: http://www.meetup.com/D-Lang-Silicon-Valley/events/224003042/ This will be a social get together with an open agenda. Bring your lightning talk and any other topic with or without a slide deck. Ali
Re: Converting uint[] slice to string for the purpose of hashing?
All types are hashable and for your own structs and classes you can redefine opHash
Re: Rant after trying Rust a bit
Walter Bright newshou...@digitalmars.com wrote: On 7/23/2015 3:12 PM, Dicebot wrote: On Thursday, 23 July 2015 at 22:10:11 UTC, H. S. Teoh wrote: OK, I jumped into the middle of this discussion so probably I'm speaking totally out of context... This is exactly one major advantage of Rust traits I have been trying to explain, thanks for putting it up in much more understandable way :) Consider the following: int foo(T: hasPrefix)(T t) { t.prefix();// ok bar(t);// error, hasColor was not specified for T } void bar(T: hasColor)(T t) { t.color(); } Now consider a deeply nested chain of function calls like this. At the bottom, one adds a call to 'color', and now every function in the chain has to add 'hasColor' even though it has nothing to do with the logic in that function. This is the pit that Exception Specifications fell into. I can see these possibilities: 1. Require adding the constraint annotations all the way up the call tree. I believe that this will not only become highly annoying, it might make generic code impractical to write (consider if bar was passed as an alias). 2. Do the checking only for 1 level, i.e. don't consider what bar() requires. This winds up just pulling the teeth of the point of the constraint annotations. 3. Do inference of the constraints. I think that is indistinguishable from not having annotations as being exclusive. Anyone know how Rust traits and C++ concepts deal with this? You may aus well ask How do interfaces in OO programming deal with this?. Frankly, I've never had an issue with that. Or it's a hint for design problems. Traits (and interfaces) are mostly not that fine grained, i.e. you don't have a trait/interface for every method. They should ideally define an abstraction/entity with a semantic meaning. If your constraint hasColor(x) just means x has method color(), and then implement it for every class that has this method, you can just as well omit constraints and use duck typing. Tobi
Re: Rant after trying Rust a bit
Walter Bright newshou...@digitalmars.com wrote: I know a lot of the programming community is sold on exclusive constraints (C++ concepts, Rust traits) rather than inclusive ones (D constraints). What I don't see is a lot of experience actually using them long term. They may not turn out so well, like ES. Haskell has type classes since ~1990. Tobi
Re: Rant after trying Rust a bit
On Thursday, 23 July 2015 at 20:09:34 UTC, Walter Bright wrote: On 7/23/2015 7:49 AM, ixid wrote: If we had a clean sheet wouldn't it be better to have if return a value and ditch ternary? Then we'd start seeing code like: x = 45 + if (y == 10) { while (i--) z += call(i); z; } else { switch (x) { case 6: foo(); y; } + tan(z); I.e. the embedding of arbitrary statements within expressions. We already have some of this with embedded anonymous lambda support, and I've discovered one needs to be very careful in formatting it to not wind up with an awful unreadable mess. So I'd be really reluctant to continue down that path. Now, if you want to disallow { } within the embedded if statement, then the proposal becomes nothing more than: ? = if : = else which is a potayto potahto thing. I agree that trivial syntax issues actually do matter, but having used ?: a lot, I have a hard time seeing embeddable if-else as a real improvement, in fact I find it more than a little jarring to see. I think I agree on the if else issue, seems arbitrary as we already have ?:. Other statements as expressions have less obvious meanings. The only part is that I wish you could have blocks as expressions. The thing is with ufcs, it really should be possible. For example the following does not compile: int a = {return 4;}; but the following does: int a = {return 4;}(); I know it's a really small difference, but with UFCS, I would expect you the be able to omit the () and have the function literal called automatically. Though I can see that this would have problems with auto and knowing if it should be a function pointer or to call the function. I guess what I would expect is auto a = {return 4;}; to type a to a function pointer, but if you explicitly type a to int then the literal should be called. Does UFCS even apply to function pointers? I guess it is a problem, it does not seem to be obvious when to call and when to copy the pointer. I don't really know what should happen. I think I read a dip a little while ago that might have addressed this, but I don't really remember. I dont know, now that I have written this, it seems to have more problems than I originally thought.
How do you make a copy TO and object when you're INSIDE of it?
Here's my code: module grammar; class Grammar(T : ulong) { this(const T[] str) { auto grammar = str in grammarCache; if (grammar) { this = grammar.dup; } else { this = approximateSmallestGrammar(str); grammarCache[str] = this.dup; } } static Grammar approximateSmallestGrammar(const T[] str) { return new Grammar(); } @property Grammar dup() { } private: this() {} static Grammar[T[]] grammarCache; }; Compiler says 'this' is not an lvalue. How would I accomplish what I want?
Re: Rant after trying Rust a bit
On Friday, 24 July 2015 at 00:55:35 UTC, Tofu Ninja wrote: On Thursday, 23 July 2015 at 20:09:34 UTC, Walter Bright wrote: On 7/23/2015 7:49 AM, ixid wrote: If we had a clean sheet wouldn't it be better to have if return a value and ditch ternary? Then we'd start seeing code like: x = 45 + if (y == 10) { while (i--) z += call(i); z; } else { switch (x) { case 6: foo(); y; } + tan(z); I.e. the embedding of arbitrary statements within expressions. We already have some of this with embedded anonymous lambda support, and I've discovered one needs to be very careful in formatting it to not wind up with an awful unreadable mess. So I'd be really reluctant to continue down that path. Now, if you want to disallow { } within the embedded if statement, then the proposal becomes nothing more than: ? = if : = else which is a potayto potahto thing. I agree that trivial syntax issues actually do matter, but having used ?: a lot, I have a hard time seeing embeddable if-else as a real improvement, in fact I find it more than a little jarring to see. I think I agree on the if else issue, seems arbitrary as we already have ?:. Other statements as expressions have less obvious meanings. The only part is that I wish you could have blocks as expressions. The thing is with ufcs, it really should be possible. For example the following does not compile: int a = {return 4;}; but the following does: int a = {return 4;}(); I know it's a really small difference, but with UFCS, I would expect you the be able to omit the () and have the function literal called automatically. Though I can see that this would have problems with auto and knowing if it should be a function pointer or to call the function. I guess what I would expect is auto a = {return 4;}; to type a to a function pointer, but if you explicitly type a to int then the literal should be called. Does UFCS even apply to function pointers? I guess it is a problem, it does not seem to be obvious when to call and when to copy the pointer. I don't really know what should happen. I think I read a dip a little while ago that might have addressed this, but I don't really remember. I dont know, now that I have written this, it seems to have more problems than I originally thought. Actually now that I think about it, I think I would expect auto a = { return 4;}; to type a to an int and call the function literal, and auto a = { return 4;}; to type a to a function pointer. I think that makes sense. Then if a is a function pointer auto b = a; would type b to a function pointer as well. I suppose UFCS really does not make sense to function pointers, but does make sense for function literals. I expect {return 4;} to just be an anonymous function, not a pointer to an anonymous function. That way you can write alias f = {return 4;}; which would just be an alias to a function, which makes sense. I haven't thought about how this would apply to delegates.
[Issue 14731] [REG2.068a] Error location insufficient when CTFE
https://issues.dlang.org/show_bug.cgi?id=14731 --- Comment #4 from github-bugzi...@puremagic.com --- Commit pushed to master at https://github.com/D-Programming-Language/dmd https://github.com/D-Programming-Language/dmd/commit/f491e5604009e40ad96cc7ecad6979975776a200 Merge pull request #4771 from 9rnsr/fix14731 --
[Issue 13720] [REG2.067] Adding trivial destructor to std.datetime causes Internal error: ..\ztc\cgelem.c 2418
https://issues.dlang.org/show_bug.cgi?id=13720 --- Comment #8 from github-bugzi...@puremagic.com --- Commit pushed to master at https://github.com/D-Programming-Language/dmd https://github.com/D-Programming-Language/dmd/commit/742f467c137c8dc6e42d0a4d0863d5bc6b0f Merge pull request #4759 from 9rnsr/fix13720 --
[Issue 14430] [REG2.060] Null parameter is detected as non-null.
https://issues.dlang.org/show_bug.cgi?id=14430 --- Comment #13 from github-bugzi...@puremagic.com --- Commit pushed to master at https://github.com/D-Programming-Language/dmd https://github.com/D-Programming-Language/dmd/commit/d6a13be63392ea2d6c2eac369a8b6e2c42b2492b Merge pull request #4775 from WalterBright/fix14430 --
[Issue 14682] [REG2.037] Incorrect interpretation of ~ []
https://issues.dlang.org/show_bug.cgi?id=14682 --- Comment #16 from github-bugzi...@puremagic.com --- Commit pushed to master at https://github.com/D-Programming-Language/dmd https://github.com/D-Programming-Language/dmd/commit/e23ff7c0e4f9de3f4dd6352e261ee430466d7ddb Merge pull request #4742 from 9rnsr/fix14682 --
[Issue 14510] Bad tail call optimization with static arrays
https://issues.dlang.org/show_bug.cgi?id=14510 --- Comment #4 from github-bugzi...@puremagic.com --- Commit pushed to master at https://github.com/D-Programming-Language/dmd https://github.com/D-Programming-Language/dmd/commit/86757a2f4efc5562d8621207676d2a187e91b33a Merge pull request #4778 from WalterBright/fix14510 --
[Issue 14626] [REG2.066] byValue doesn't work with inout AA
https://issues.dlang.org/show_bug.cgi?id=14626 --- Comment #7 from github-bugzi...@puremagic.com --- Commits pushed to master at https://github.com/D-Programming-Language/druntime https://github.com/D-Programming-Language/druntime/commit/6d57c68df1f72e9416548ad450e56a16bb1fdb90 fix Issue 14626 - byValue doesn't work with inout AA https://github.com/D-Programming-Language/druntime/commit/ad900eb3cc38397c4fa3a0a805793f002d03abc7 Merge pull request #1326 from 9rnsr/fix14626 --
Re: casting away const and then mutating
On Friday, 24 July 2015 at 03:02:30 UTC, Steven Schveighoffer wrote: On 7/23/15 7:57 PM, Jonathan M Davis wrote: If you cast away const, it's up to you to guarantee that the data being referenced is not mutated, and if it is mutated, it's undefined behavior. Still need a reference to the spec that says that. Note that the spec specifically says it's undefined to cast away and modify immutable, and is careful not to include const/mutable in that discussion. It's come up a number of times in discussions on logical const, including from Walter. For it to be otherwise would mean that const is not actually physical const. I'm quite certain that the spec is wrong in this case. OK, but the point is you have run an algorithm that gets a *piece* of x (pretend x is not just a simple int), which you know to be mutable because x is mutable. But you don't want the algorithm to mutate x. Basically, if we say this is undefined behavior, then inout is undefined behavior. inout is done by the compiler. It knows that it's safe to cast the return type to mutable (or immutable), because it knows that the return value was either the argument that it passed in or something constructed within the function and thus safe to cast. The compiler knows what's going on, so it can ensure that it doesn't violate the type system and is well-defined. An example of what the compiler can start doing more than it does now would be helpful. I can't see how it can do anything based on this. Well, it could remove dead code. For instance, if you had const(Foo) bar(T t) { const myFoo = getFoo(t); auto value1 = pureFunc(myFoo); auto value2 = pureFunc2(myFoo); auto value3 = pureFunc3(value1, value2); return myFoo; } All of the lines with pureFunc* could be removed outright, because they're all pure function calls, and they can't possibly have mutated myFoo. I wouldn't expect a lot of dead code like that, and maybe something like that would never be implemented in the compiler, but it could be as long as the compiler can actually rely on const not being mutated. But part of the problem with start doing more than it does now is that that could easily depend on ideas that folks come up with later. At some point in the future, someone might figure out how const interacts with some other set of attributes and be able to optimize based on that. So, if you're casting away const and mutating, relying on no one coming up with new optimizations, then you could be in trouble later when they do. And maybe they won't, but we don't know. Specifically, I would say you can cast away const on a reference that you have created within your own function on a mutable piece of data (in other words, you control the mutable data), then you can mutate via the cast reference. Otherwise, the inout feature is invalid, and we should remove it from the language, because that's EXACTLY what it does. A simple example: struct Node { int val; Node *next; } const(Node) *find(const(Node)* n, int val) { while(n n.val != val) n = n.next; return n; } Node *find(Node *n, int val) { const cn = n; return cast(Node *)find(cn, val); } Note that the mutable version of find doesn't mutate the node (checked by the compiler BTW), and it's signature doesn't allow any const optimizations -- it gets in a mutable and returns a mutable. This can be rewritten like this: inout(Node) *find(inout(Node)* n, int val) { while(n n.val != val) n = n.next; return n; } But in the case of the PR in question, we can't do this, because we can't inout our range and have it continue to be a range. So we are mimicking the behavior of inout, and the compiler should be fine with this. It sounds like what you really want is a tail-inout range or somesuch, though since we can't even sort out tail-const ranges properly at this point, I expect that tail-inout ranges are a bit of a pipe dream. In any case, regardless of whether what you're proposing defined behavior or not, it'll work, because there's no way that the compiler could do any optimizations based on const after the cast is done, because it's only const within the function. It's when you cast away const on something that you were given as const that you have a real problem. e.g. const(Foo) bar(const(Foo) foo) { auto f = cast(Foo)foo; f.mutateMe(); return foo; } I agree that what you're asking for makes sense. If you pass in a mutable object to a function, and you know that the const object you get out is either the same object or a new one that is not immutable, and no references to that object escaped the function, then casting the return type to mutable should work, and I'm not against that being well-defined, but as I understand it, it technically isn't, because it involves casting away const and then mutating the result. And if it is well-defined, then we'd need clear way to describe the circumstances to
Re: Where will D sit in the web service space?
On Thursday, 23 July 2015 at 17:44:59 UTC, Etienne wrote: On Thursday, 23 July 2015 at 17:03:31 UTC, Laeeth Isharc wrote: But some of us think general-purpose, native languages are coming back, Yes. Now why do you think this is the case? I tried to articulate it as best I could for now, but Ola has all these _reasons_ why this isn't the case, which may mean he is right, but might not. Native languages are more efficient, they use less power. This is increasingly important to reduce the greenhouse gas emissions, to improve battery duration on mobile devices or to reduce server costs in general. Yep, I specifically mentioned the mobile and server domains as places where general-purpose native/AoT-compiled languages are having a resurgence, obviously for the efficiency reasons Etienne lists. Being general-purpose simply means that you wouldn't be limited to one of those domains, and could quickly bridge over to even newer domains that spring up. One big trend over the last decade and a half has been the rise of webapps, where native desktop apps, which are still predominantly written in native languages, have been de-emphasized as a result. However, with the rise of mobile and webapps not doing as well there, for a variety of reasons, native development is coming back for many apps, at least on the client side for networked apps. On the server, as long as you don't really need to scale out, you have a lot of choices for your tech. But the moment you need to scale, you'll probably want to go native, at least for your backend.
Re: How do you make a copy TO and object when you're INSIDE of it?
On 7/23/15 9:30 PM, Enjoys Math wrote: Here's my code: module grammar; class Grammar(T : ulong) { this(const T[] str) { auto grammar = str in grammarCache; if (grammar) { this = grammar.dup; } else { this = approximateSmallestGrammar(str); grammarCache[str] = this.dup; } } static Grammar approximateSmallestGrammar(const T[] str) { return new Grammar(); } @property Grammar dup() { } private: this() {} static Grammar[T[]] grammarCache; }; Compiler says 'this' is not an lvalue. How would I accomplish what I want? You're approaching this wrong. Do the lookup before deciding whether to instantiate a new object: static Grammar getGrammar(const T[] str) { if(auto x = str in grammarCache) return *x; else { auto g = new Grammar; grammarCache[str] = g; return g; } } If you always want to dup, then do it outside the lookup. Don't do it in the constructor, you already have an object by then. -Steve
[Issue 14822] DMD crash with CTFE range code (v2.067.1)
https://issues.dlang.org/show_bug.cgi?id=14822 Kenji Hara k.hara...@gmail.com changed: What|Removed |Added Status|NEW |RESOLVED Resolution|--- |WORKSFORME --- Comment #1 from Kenji Hara k.hara...@gmail.com --- The issue does not happen with 2.068-beta1.. I guess the root issue had had a problem in error handling for the template instantiation. I think it has been fixed by: https://github.com/D-Programming-Language/dmd/pull/4464 --
Re: Rant after trying Rust a bit
On 7/23/2015 6:05 PM, H. S. Teoh via Digitalmars-d wrote: It doesn't solve *all* the problems, but it does solve a significant subset of them. The worst case of not having this feature is a compile time error. Not a runtime error, undetectable error, or silent corruption. A compile time error. I also believe that you underestimate the nuisance significance of requiring the constraints cover 100% of everything the template body does. Experience with something similar is with exception specifications. Even advocates of ES found themselves writing obviously crap code to work around the issue, because ES was so damned annoying. I know a lot of the programming community is sold on exclusive constraints (C++ concepts, Rust traits) rather than inclusive ones (D constraints). What I don't see is a lot of experience actually using them long term. They may not turn out so well, like ES.
Re: Rant after trying Rust a bit
On Thursday, 23 July 2015 at 21:27:17 UTC, Ziad Hatahet wrote: I think it is actually kinda pretty: What about: int median(int a, int b, int c) { return (ab) ? (bc) ? b : (ac) ? c : a : (ac) ? a : (bc) ? c : b; } vs. def median(a: Int, b: Int, c: Int) = if (a b) { if (b c) b else if (a c) c else a } else if (a c) a else if (b c) c else b Not really a spaces-to-spaces comparison... to be honest, I'd probably just write that as: int median(int a, int b, int c) { if (a b) { if (b c) return b; else if (a c) return c; else return a; } else if (a c) return a; else if (b c) return c; else return b; } You don't need it to be an expression since it is a function, you can simply write return statements (which I kinda like since then it is obvious that that value is a terminating condition and not just the middle of some other calculation). But if we were using a ternary, some newlines can help with it: return (a b) ? ( (b c) ? b : (a c) ? c : a ) : (a c) ? a : (b c) ? c : b; Indeed, there I just took your if/else thing and swapped out the else keyword for the : symbol, then replaced if(cond) with (cond) ?, then changed out the {} for (). It still works the same way. Is the compiler always able to always optimize out the function call by inlining it, as would be the case with a scope? It should be.
Re: casting away const and then mutating
On 7/23/15 7:57 PM, Jonathan M Davis wrote: On Thursday, 23 July 2015 at 18:43:03 UTC, anonymous wrote: On a GitHub pull request, Steven Schveighoffer (schveiguy), Jonathan M Davis (jmdavis), and I (aG0aep6G) have been discussing if or when it's ok to cast away const and then mutate the data: https://github.com/D-Programming-Language/phobos/pull/3501#issuecomment-124169544 I've been under the impression that it's never allowed, i.e. it's always undefined behaviour. I think Jonathan is of the same opinion. It's come up time and time again with discussions for logical const. This is not logical const. We are starting with mutable data, moving it through a function that we *don't* want to mutate the data, and then using it as mutable again in the function where you (and the compiler) know its mutable. But you aren't even mutating, just getting it back to the original constancy (though mutation should be OK, you still have a mutable reference). If you cast away const, it's up to you to guarantee that the data being referenced is not mutated, and if it is mutated, it's undefined behavior. Still need a reference to the spec that says that. Note that the spec specifically says it's undefined to cast away and modify immutable, and is careful not to include const/mutable in that discussion. Now, if you know that the data being referenced is actually mutable and not immutable, and you know that the compiler isn't going to make any assumptions based on const which are then wrong if you mutate the variable after casting away const, then you can get away with it. But it's still undefined behavior, and if the compiler later starts doing more than it does now based on the knowledge that you can't mutate via a const reference, then your code might stop working correctly. So, if you're _really_ careful, you can get away with casting away const and mutating a variable, but you are depending on undefined behavior. An example of what the compiler can start doing more than it does now would be helpful. I can't see how it can do anything based on this. int x; const int *y = x; *(cast(int *)y) = 5; Even if this were defined behavior, what would be the point? You have access to x. You could just mutate it directly. OK, but the point is you have run an algorithm that gets a *piece* of x (pretend x is not just a simple int), which you know to be mutable because x is mutable. But you don't want the algorithm to mutate x. Basically, if we say this is undefined behavior, then inout is undefined behavior. I don't see how it would make any sense to be attempting to mutate something via a const reference when you have access to it via a mutable reference. It's when you don't have access to it via a mutable reference that it becomes an issue - which means that you've crossed a function boundary. Exactly. You still have mutable access to it, and you know the const access is to the same object, just transformed via an algorithm. The only way to make casting away const and mutating defined behavior in general This is NOT what is being asked. Not the general case of making it defined to cast away const on any item (which could turn out to be immutable). I think it's pointless to argue over this. The behavior can't be defined any other way than what I'm asking. The question is if we want the *official* position to nonsensically call it undefined behavior. Specifically, I would say you can cast away const on a reference that you have created within your own function on a mutable piece of data (in other words, you control the mutable data), then you can mutate via the cast reference. Otherwise, the inout feature is invalid, and we should remove it from the language, because that's EXACTLY what it does. A simple example: struct Node { int val; Node *next; } const(Node) *find(const(Node)* n, int val) { while(n n.val != val) n = n.next; return n; } Node *find(Node *n, int val) { const cn = n; return cast(Node *)find(cn, val); } Note that the mutable version of find doesn't mutate the node (checked by the compiler BTW), and it's signature doesn't allow any const optimizations -- it gets in a mutable and returns a mutable. This can be rewritten like this: inout(Node) *find(inout(Node)* n, int val) { while(n n.val != val) n = n.next; return n; } But in the case of the PR in question, we can't do this, because we can't inout our range and have it continue to be a range. So we are mimicking the behavior of inout, and the compiler should be fine with this. -Steve
[Issue 14806] [REG2.063] alias this doesn't force elaborate equality, but is followed during it
https://issues.dlang.org/show_bug.cgi?id=14806 --- Comment #3 from github-bugzi...@puremagic.com --- Commits pushed to master at https://github.com/D-Programming-Language/dmd https://github.com/D-Programming-Language/dmd/commit/83fa6accb7b83f3a88898a675933319073e3497e fix Issue 14806 - alias this doesn't force elaborate equality, but is followed during it https://github.com/D-Programming-Language/dmd/commit/9318101daf48fb24b335cb6177c509bfec390586 Merge pull request #4820 from 9rnsr/fix14806 --
[Issue 14753] pragma(inline) hides the alias string
https://issues.dlang.org/show_bug.cgi?id=14753 --- Comment #4 from github-bugzi...@puremagic.com --- Commit pushed to master at https://github.com/D-Programming-Language/dmd https://github.com/D-Programming-Language/dmd/commit/cbab6d6303783a22ee65a5a1e777a50d038133fa Merge pull request #4793 from 9rnsr/fix14753 --
[Issue 14737] [REG2.058] A concatenation of array literal and static array should make dynamic array
https://issues.dlang.org/show_bug.cgi?id=14737 --- Comment #6 from github-bugzi...@puremagic.com --- Commit pushed to master at https://github.com/D-Programming-Language/dmd https://github.com/D-Programming-Language/dmd/commit/d68e8d61ccabd1ae7c2252b86cda56e13adef1c4 Merge pull request #4776 from 9rnsr/fix14737 --
[Issue 14572] cannot build dmd from source anymore: 'g++ -m64: No such file or directory'
https://issues.dlang.org/show_bug.cgi?id=14572 --- Comment #9 from github-bugzi...@puremagic.com --- Commit pushed to master at https://github.com/D-Programming-Language/dmd https://github.com/D-Programming-Language/dmd/commit/65ce308abc30e0f31fa118326840da14043adf5f Merge pull request #4772 from JinShil/fix14572 --
[Issue 14735] [REG2.068-b1] std.string.indexOf cannot deduce function for char argument
https://issues.dlang.org/show_bug.cgi?id=14735 --- Comment #15 from github-bugzi...@puremagic.com --- Commit pushed to master at https://github.com/D-Programming-Language/dmd https://github.com/D-Programming-Language/dmd/commit/c2b84a0b24f680bfa3cd336af6c84b0ded2c4594 Merge pull request #4779 from 9rnsr/fix14735 --
Re: How do you make a copy TO and object when you're INSIDE of it?
On Friday, 24 July 2015 at 03:12:43 UTC, Steven Schveighoffer wrote: On 7/23/15 9:30 PM, Enjoys Math wrote: [...] You're approaching this wrong. Do the lookup before deciding whether to instantiate a new object: static Grammar getGrammar(const T[] str) { if(auto x = str in grammarCache) return *x; else { auto g = new Grammar; grammarCache[str] = g; return g; } } If you always want to dup, then do it outside the lookup. Don't do it in the constructor, you already have an object by then. -Steve Thanks. That sounds like a good approach
Re: Rant after trying Rust a bit
On 7/23/2015 3:12 PM, Dicebot wrote: On Thursday, 23 July 2015 at 22:10:11 UTC, H. S. Teoh wrote: OK, I jumped into the middle of this discussion so probably I'm speaking totally out of context... This is exactly one major advantage of Rust traits I have been trying to explain, thanks for putting it up in much more understandable way :) Consider the following: int foo(T: hasPrefix)(T t) { t.prefix();// ok bar(t);// error, hasColor was not specified for T } void bar(T: hasColor)(T t) { t.color(); } Now consider a deeply nested chain of function calls like this. At the bottom, one adds a call to 'color', and now every function in the chain has to add 'hasColor' even though it has nothing to do with the logic in that function. This is the pit that Exception Specifications fell into. I can see these possibilities: 1. Require adding the constraint annotations all the way up the call tree. I believe that this will not only become highly annoying, it might make generic code impractical to write (consider if bar was passed as an alias). 2. Do the checking only for 1 level, i.e. don't consider what bar() requires. This winds up just pulling the teeth of the point of the constraint annotations. 3. Do inference of the constraints. I think that is indistinguishable from not having annotations as being exclusive. Anyone know how Rust traits and C++ concepts deal with this?
Re: Rant after trying Rust a bit
On 7/23/2015 3:06 PM, H. S. Teoh via Digitalmars-d wrote: OK, I jumped into the middle of this discussion so probably I'm speaking totally out of context... but anyway, with regards to template code, I agree that it ought to be thoroughly tested by at least instantiating the most typical use cases (as well as some not-so-typical use cases). I argue that every code line of the template must at least have been instantiated at some point by the test suite. Anything less is, frankly, unprofessional. A lot of Phobos bugs lurk in rarely-used template branches that are not covered by the unittests. Generally when I work on a Phobos template, I upgrade it to 100% unit test coverage. This should be a minimum bar for all Phobos work. We ought to be ashamed of anything less. Instantiating all branches is only part of the solution, though. A lot of Phobos bugs also arise from undetected dependencies of the template code on the specifics of the concrete types used to test it in the unittests. The template passes the unittest but when you instantiate it with a type not used in the unittests, it breaks. For instance, a lot of range-based templates are tested with arrays in the unittests. Some of these templates wrongly depend on array behaviour (as opposed to being confined only to range API operations) while their signature constraints indicate only the generic range API. As a result, when non-array ranges are used, it breaks. Sometimes bugs like this can lurk undetected for a long time before somebody one day happens to instantiate it with a range type that violates the hidden assumption in the template code. I agree that the constraint system is not checked against the actual body of the template. Dicebot brought that up as well. Some attention should be paid in the unit tests to using types that are minimal implementations of the constraints. That said, it is a pipe dream to believe that if something matches the function signatures, that it is correct and will work without ever having been tested. If we had a Concepts-like construct in D, where template code is statically constrained to only use, e.g., range API when manipulating an incoming type, a lot of these bugs would've been caught. In fact, I'd argue that this should be done for *all* templates -- for example, a function like this ought to be statically rejected: auto myFunc(T)(T t) { return t + 1; } because it assumes the validity of the + operation on T, but T is not constrained in any way, so it can be *any* type, most of which, arguably, do not support the + operation. It's a valid point, but I'd counter that it'd be pretty tedious and burdensome. D isn't meant to be a bondage discipline language. The failed exception specifications (Java and C++) comes to mind. If the compiler outright rejected any operation on T that hasn't been explicitly tested for, *then* we will have eliminated a whole class of template bugs. Wrong code like the last example above would be caught as soon as the compiler compiles the body of myFunc. Yeah, but few would like programming in such a nagging, annoying language. Note that if you do instantiate with a type that doesn't support those operations, it isn't the end of the world - you'll still get a compile time error message.
Re: Measuring Execution time
On Thursday, July 23, 2015 13:59:11 Steven Schveighoffer via Digitalmars-d-learn wrote: On 7/22/15 5:23 AM, Clayton wrote: How does one represent Duration in only Micro-seconds, or milliseconds. Trying to measure the execution time of an algorithm and I get 4 ms, 619 μs, and 8 hnsecs , I want to sum all these and get total hnsecs or μs . I would also appreciate advise on whether this is the best way to measure the execution time of an algorithm. import std.datetime; import std.stdio; void algorithm( ){ writeln(Hello!); } void main(){ auto stattime = Clock.currTime(); algorithm( ); endttime = Clock.currTime(); auto duration = endttime - stattime; writeln(Hello Duration == , duration); } I know John identified Stopwatch, but just an FYI, Duration has the method total: http://dlang.org/phobos/core_time.html#.Duration.total I think doing: writeln(Hello Duration == , duration.total!usecs); would also work. Yes, you could do that, but doing timing with the realtime clock is fundamentally wrong, because the clock can change on you while you're timing. That's why using a monotonic clock is better, since it's guaranteed to never move backwards. Unfortunately, while StopWatch does use a monotonic clock, it currently does that by using TickDuration for that rather than MonoTime, so its result is a TickDuration rather than a Duration, so it's a bit harder to use than would be nice, but it is more correct to use StopWatch than to subtract SysTimes. Alternatively, you could just use MonoTime directly. e.g. auto startTime = MonoTime.currTime; // do stuff auto endTime = MonoTime.currTime; audo duration = endTime - startTime; writeln(Hello Duration == , duration.total!usecs); in which case you get a Duration just like with subtract SysTimes, and the suggestion of using total works just fine. I need to put together replacements for the benchmarking functions in std.datetime (probably in std.benchmark) which use MonoTime and Duration rather than TickDuration so that we can deprecate the ones in std.datetime which use TickDuration (and deprecate TickDuration itself). - Jonathan M Davis
Re: Rant after trying Rust a bit
On Thu, Jul 23, 2015 at 05:34:20PM -0700, Walter Bright via Digitalmars-d wrote: On 7/23/2015 3:06 PM, H. S. Teoh via Digitalmars-d wrote: OK, I jumped into the middle of this discussion so probably I'm speaking totally out of context... but anyway, with regards to template code, I agree that it ought to be thoroughly tested by at least instantiating the most typical use cases (as well as some not-so-typical use cases). I argue that every code line of the template must at least have been instantiated at some point by the test suite. Anything less is, frankly, unprofessional. I agree, and I didn't claim otherwise. A lot of Phobos bugs lurk in rarely-used template branches that are not covered by the unittests. Generally when I work on a Phobos template, I upgrade it to 100% unit test coverage. This should be a minimum bar for all Phobos work. We ought to be ashamed of anything less. Agreed. Instantiating all branches is only part of the solution, though. A lot of Phobos bugs also arise from undetected dependencies of the template code on the specifics of the concrete types used to test it in the unittests. The template passes the unittest but when you instantiate it with a type not used in the unittests, it breaks. For instance, a lot of range-based templates are tested with arrays in the unittests. Some of these templates wrongly depend on array behaviour (as opposed to being confined only to range API operations) while their signature constraints indicate only the generic range API. As a result, when non-array ranges are used, it breaks. Sometimes bugs like this can lurk undetected for a long time before somebody one day happens to instantiate it with a range type that violates the hidden assumption in the template code. I agree that the constraint system is not checked against the actual body of the template. Dicebot brought that up as well. Some attention should be paid in the unit tests to using types that are minimal implementations of the constraints. That said, it is a pipe dream to believe that if something matches the function signatures, that it is correct and will work without ever having been tested. I didn't say that this one thing alone will singlehandedly solve all of our template testing woes. Obviously, it cannot catch semantic errors -- you use all the valid range API operations, but you use them in the wrong order, say, or in a way that doesn't accomplish what the code is supposed to do. I think it's a given that you still need to adequately unittest the code just like you would non-template code. Nevertheless, this does help to eliminate an entire class of latent template bugs -- hidden dependencies on the incoming type that are not covered by the function's contract (i.e., signature constraints). Relying on the programmer to always use types with minimal functionality in the unittests is programming by convention, and you know very well how effective that is. Without enforcement, we have no way of being sure that our tests are actually adequate. An untested branch of template code can be detected by using -cov, but performing an operation on an incoming type without checking for it in the sig constraints cannot be detected except by reading every line of code. The unittest may have inadvertently used a type with a superset of functionality, but since this is never enforced (and the current language provides no way to actually enforce it) we can never be sure -- we're just taking it on faith that the tests have covered all bases. With actual language enforcement, we can actually provide some guarantees. It doesn't solve *all* the problems, but it does solve a significant subset of them. If we had a Concepts-like construct in D, where template code is statically constrained to only use, e.g., range API when manipulating an incoming type, a lot of these bugs would've been caught. In fact, I'd argue that this should be done for *all* templates -- for example, a function like this ought to be statically rejected: auto myFunc(T)(T t) { return t + 1; } because it assumes the validity of the + operation on T, but T is not constrained in any way, so it can be *any* type, most of which, arguably, do not support the + operation. It's a valid point, but I'd counter that it'd be pretty tedious and burdensome. D isn't meant to be a bondage discipline language. The failed exception specifications (Java and C++) comes to mind. If the compiler outright rejected any operation on T that hasn't been explicitly tested for, *then* we will have eliminated a whole class of template bugs. Wrong code like the last example above would be caught as soon as the compiler compiles the body of myFunc. Yeah, but few would like programming in such a nagging, annoying language. I have trouble thinking of a template function that's actually *correct* when its sig constraints doesn't specify what operations are valid on the
Re: How do you make a copy TO and object when you're INSIDE of it?
On Friday, July 24, 2015 01:30:55 Enjoys Math via Digitalmars-d-learn wrote: Here's my code: module grammar; class Grammar(T : ulong) { this(const T[] str) { auto grammar = str in grammarCache; if (grammar) { this = grammar.dup; } else { this = approximateSmallestGrammar(str); grammarCache[str] = this.dup; } } static Grammar approximateSmallestGrammar(const T[] str) { return new Grammar(); } @property Grammar dup() { } private: this() {} static Grammar[T[]] grammarCache; }; Compiler says 'this' is not an lvalue. How would I accomplish what I want? Assign to the members individually rather than the whole object at once. - Jonathan M Davis
[Issue 14699] [REG2.062] ICE: segfaults on array with zero size
https://issues.dlang.org/show_bug.cgi?id=14699 --- Comment #12 from github-bugzi...@puremagic.com --- Commit pushed to master at https://github.com/D-Programming-Language/dmd https://github.com/D-Programming-Language/dmd/commit/72f759961d08ed9a4e0da2e1d81608fed9a0d7b6 Merge pull request #4744 from 9rnsr/fix14699 --
[Issue 14805] [REG2.064] Unreasonably slow new Struct[large]
https://issues.dlang.org/show_bug.cgi?id=14805 --- Comment #7 from github-bugzi...@puremagic.com --- Commits pushed to master at https://github.com/D-Programming-Language/dmd https://github.com/D-Programming-Language/dmd/commit/27dc11712eb4ddd4d45033654f7d8cb93d7745d4 fix Issue 14805 - Unreasonably slow new Struct[large] https://github.com/D-Programming-Language/dmd/commit/35d35f1cf7c5e386f3c9c2f5ca76fc4f12f174e0 Merge pull request #4821 from 9rnsr/fix14805 --
[Issue 14754] [REG2.068b1] 64bit wrong code with -inline
https://issues.dlang.org/show_bug.cgi?id=14754 --- Comment #6 from github-bugzi...@puremagic.com --- Commit pushed to master at https://github.com/D-Programming-Language/dmd https://github.com/D-Programming-Language/dmd/commit/22e27f5d84dc39d005106303986d91525e2834bf Merge pull request #4795 from 9rnsr/fix14754 --
Re: Rant after trying Rust a bit
On 7/23/2015 7:15 AM, Andrei Alexandrescu wrote: I am a bit puzzled by the notion of shipping template code that has never been instantiated as being a positive thing. This has also turned up in the C++ static_if discussions. This is easy to understand. Weeding out uncovered code during compilation is a central feature of C++ concepts. Admitting you actually never want to do that would be a major blow. But if a unit test fails at instantiating it, it fails at compile time.
Re: Rant after trying Rust a bit
On Thu, Jul 23, 2015 at 12:49:29PM -0700, Walter Bright via Digitalmars-d wrote: On 7/23/2015 7:15 AM, Andrei Alexandrescu wrote: I am a bit puzzled by the notion of shipping template code that has never been instantiated as being a positive thing. This has also turned up in the C++ static_if discussions. This is easy to understand. Weeding out uncovered code during compilation is a central feature of C++ concepts. Admitting you actually never want to do that would be a major blow. But if a unit test fails at instantiating it, it fails at compile time. That assumes the template author is diligent (foolhardy?) enough to write unittests that cover all possible instantiations... T -- People say I'm indecisive, but I'm not sure about that. -- YHL, CONLANG
Re: Rant after trying Rust a bit
Andrei Alexandrescu seewebsiteforem...@erdani.org wrote: On 7/23/15 5:07 AM, Walter Bright wrote: Turns out many constraints in Phobos are of the form (A || B), not just (A B). Agreed. And that's just scratching the surface. Serious question: how do you express in Rust that a type implements one trait or another, then figure out statically which? You define a new trait and implement it differently for A and B. That leads to a cleaner design IMO because you have to think about the right abstraction for that trait. TBH I'm very surprised about that argument, because boolean conditions with version() were dimissed for exactly that reason. Tobi
Re: Rant after trying Rust a bit
On 7/23/2015 8:03 AM, Dicebot wrote: At the same time one HUGE deal breaker with rust traits that rarely gets mentioned is the fact that they are both constraints and interfaces at the same time: // this is template constraint, it will generate new `foo` symbol for each new T fn foo T : InputRange (range : T) // this use the very same trait definition but creates fat pointer on demand with simplistic dispatch table fn foo (range : InputRange) It kills all the necessity for hacks like RangeObject and is quite a salvation once you get to defining dynamic shared libraries with stable ABI. This is probably my most loved feature of Rust. D interface types also produce the simplistic dispatch table, and if you make them extern(C++) they don't need a RangeObject. I know it isn't as convenient as what you describe above, but it can be pressed into service.
Re: Rant after trying Rust a bit
jmh530 john.michael.h...@gmail.com wrote: I feel like it's hard to separate borrowing from Rust's variety of pointers ( is borrowed pointer, ~ is for unique pointer, @ is for managed pointer). That's actually very outdated information. Two of the four pointer types (,@,~ and *) were ditched in favor of library solutions. *T: Raw pointers are still the same T: is now called reference, not borrowed pointer ~T: Is now BoxT @T: Is now RcT (and possibly GcT although a GC was never implemented) Tobi
Re: Rant after trying Rust a bit
On Thursday, 23 July 2015 at 19:55:30 UTC, Walter Bright wrote: On 7/23/2015 8:03 AM, Dicebot wrote: At the same time one HUGE deal breaker with rust traits that rarely gets mentioned is the fact that they are both constraints and interfaces at the same time: // this is template constraint, it will generate new `foo` symbol for each new T fn foo T : InputRange (range : T) // this use the very same trait definition but creates fat pointer on demand with simplistic dispatch table fn foo (range : InputRange) It kills all the necessity for hacks like RangeObject and is quite a salvation once you get to defining dynamic shared libraries with stable ABI. This is probably my most loved feature of Rust. D interface types also produce the simplistic dispatch table, and if you make them extern(C++) they don't need a RangeObject. I know it isn't as convenient as what you describe above, but it can be pressed into service. I am not sure how it applies. My point was about the fact that `isInputRange` and `InputRangeObject` are the same entities in Rust, simply interpreted differently by compiler depending on usage context. This is important because you normally want to design your application in terms of template constraints and structs to get most out of inlining and optimization. However, to define stable ABI for shared libraries, the very same interfaces need to be wrapped in runtime polymorphism. Closest thing in D would be to define traits as interfaces and use code like this: void foo(T)() if ( (is(T == struct) || is(T == class)) Matches!(T, Interface) ) { } where `Matches` is a template helper that statically iterates method list of interface and looks for matching methods in T. However, making it built-in feels really convenient in Rust: - considerably less function declaration visual noise - much better error messages: trying to use methods of T not defined by a trait will result in compile-time error even without instantiating the template
Re: Rant after trying Rust a bit
On 7/23/2015 7:49 AM, ixid wrote: If we had a clean sheet wouldn't it be better to have if return a value and ditch ternary? Then we'd start seeing code like: x = 45 + if (y == 10) { while (i--) z += call(i); z; } else { switch (x) { case 6: foo(); y; } + tan(z); I.e. the embedding of arbitrary statements within expressions. We already have some of this with embedded anonymous lambda support, and I've discovered one needs to be very careful in formatting it to not wind up with an awful unreadable mess. So I'd be really reluctant to continue down that path. Now, if you want to disallow { } within the embedded if statement, then the proposal becomes nothing more than: ? = if : = else which is a potayto potahto thing. I agree that trivial syntax issues actually do matter, but having used ?: a lot, I have a hard time seeing embeddable if-else as a real improvement, in fact I find it more than a little jarring to see.
Re: Rant after trying Rust a bit
On Thu, Jul 23, 2015 at 8:41 AM, Jonathan M Davis via Digitalmars-d digitalmars-d@puremagic.com wrote: Maybe, but the ternary operator is a lot less verbose The ternary operator becomes much harder to read the moment you have more than the simple if/else case. As it was mentioned elsewhere on this thread, you can do the following in Scala: val x = if (condition_1) 1 else if (condition_2) 2 else if (condition_3) 3 else 4 Having expressions be built-in extends beyond the simple if/else case, which can be emulated with the ternary operator as you said. You can assign the result of match expressions for instance, or the result of scoped blocks, .e.g. val x = { val ys = foo() ys.map(...).filter(...).exists(...) } , and from some other comments in this thread, it sounds like the way they implemented it in Rust forces you to use braces for single line statements, which would be a _huge_ downside IMHO. On the other hand, Rust does not require parenthesis around if conditions: let x = if some_condition { 1 } else { 2 } I'm inclined to think that it would need a use case that's a lot more compelling than if-else chains to be worth it. I provided examples above. -- Ziad
Re: Rant after trying Rust a bit
On Thu, Jul 23, 2015 at 08:50:55PM +, Vlad Levenfeld via Digitalmars-d wrote: On Thursday, 23 July 2015 at 20:40:17 UTC, Walter Bright wrote: On 7/23/2015 12:50 PM, H. S. Teoh via Digitalmars-d wrote: That assumes the template author is diligent (foolhardy?) enough to write unittests that cover all possible instantiations... No, only each branch of the template code must be instantiated, not every possible instantiation. And we have a tool to help with that: -cov Does anyone believe it is a good practice to ship template code that has never been instantiated? I dunno about good practices but I have some use cases. I write a bunch of zero-parameter template methods and then pass them into a Match template which attempts to instantiate each of them in turn, settling on the first one which does compile. So the methods basically form a list of preferred implementation of functionality X. All but one winds up uninstantiated. [...] But don't you still have to test each template, to make sure they compile when they're supposed to? T -- Without geometry, life would be pointless. -- VS
Overloading Based on Constraints
I was looking at http://dlang.org/concepts.html where it discusses overloading templates based on constraints. I wanted to try to overload a template function with another version that does everything in place. So basically, one has a return and the other doesn't. However, when I run the code, it only calls the first function. The second one is ignored. I tried a number of adjustments, but the only thing that worked is to re-name the function something else, remove the U's, and just have it be a void function. import std.stdio : writeln; import std.traits; T test(T)(T x) if (isNumeric!(T)) { writeln(calling test without void); T y = x; y += 1; return y; } U test(T, U)(ref T x) if (isNumeric!(T) is(U == void)) { writeln(calling test with void); x += 2; } void main() { int a = 1; int b = test(a); writeln(b); int c = b; test(c); writeln(c); }
Re: Rant after trying Rust a bit
On Thu, 23 Jul 2015 13:46:16 -0700, Walter Bright wrote: like implicit declaration of variables. Trigger warning needed!
Re: Rant after trying Rust a bit
On Thursday, 23 July 2015 at 22:10:11 UTC, H. S. Teoh wrote: OK, I jumped into the middle of this discussion so probably I'm speaking totally out of context... This is exactly one major advantage of Rust traits I have been trying to explain, thanks for putting it up in much more understandable way :)
Re: dmd 2.068, 2.069, 2.0xx Evil Plan going forward
On Mon, 20 Jul 2015 21:49:00 +, Martin Nowak wrote: We have a PR from Kenji, that fixes 313+314. https://github.com/D-Programming-Language/dmd/pull/3407 It's a major change of the import system, so it needs a thorough review and we also need to mitigate the code breakage impact of this change. at least for phobos, it breaks almost nothing. and where it does some breckage, it identifies invalid code that relies on import bugs anyway. the same with some projects i tried to build, like deadcode, for example (which is fairly big): this PR breaks only invalid code, i.e. code that relies on current buggy import and should be fixed anyway. yet, there is surprisingly small amount of such code, literally along 10 or so patches for phobos (and that number constantly decreases), and not much more for deadcode (but for deadcode the fixes was a little more complicated, as i have to add some qualifiers in some modules). i'm using DMD with that patch incorporated for more a year now, and got no problems with code that doesn't rely on import bugs. signature.asc Description: PGP signature
Re: casting away const and then mutating
On 7/23/15 2:43 PM, anonymous wrote: Steven disagrees and thinks that there are cases where it's ok. Namely, this simple case would be ok: int x; const int *y = x; *(cast(int *)y) = 5; Yes, IMO, this should simply work and be consistent. The compiler could use willful ignorance to assume x is still 0, but I find that to be counterproductive. It would have to implement flow analysis to determine that y must point at x, and then simply ignore the assignment during that analysis. I'll note that the reason I want to allow this, is because we have a case where the same implementation must be used for the const and mutable version of the function, but the return value is a template that varies on constancy. Therefore, you necessarily need 2 function definitions -- the compiler isn't smart enough (or maybe it's too smart?) to use inout(T) as a template parameter to the Range struct, and auto convert that back on return. The nice thing about inout, is the compiler guarantees one implementation of the function, and the implementation will guarantee const is preserved. But on returning, the function puts the data back to the way it was. That's exactly what we want. In this case, we can't use inout. So we have to cast (or alternatively, copy-paste implementation) one result to the other. My opinion is, we should execute the implementation as if the object were const, and then cast back to mutable if we are using the mutable entry point. This allows the compiler to check the function implementation for const-correctness, vs. the other suggestion: casting the const object to a mutable one and then hoping the mutable function implementation is const-correct without compiler help. The proposed usage of casting also does not mutate the data in the function that casts. It receives in a mutable object, and it outputs a reference to the mutable object. The compiler would have to go through great lengths to see that the source of the mutable range it's receiving comes from a const range, and then ignore the type system in order to elide a reloading of something that is miniscule compared to the function call itself. As I understand him, he's arguing that since the data is mutable, and since no function boundaries are crossed, compilers should not be allowed to do anything but the obvious with that code. I think we've exchanged all arguments we have, yet no one has been convinced by the other side. We've found the language spec to be a bit sparse on this. All I could find is essentially you can't mutate through a const reference [1], but no mention of if/when it's ok to cast a const reference to a mutable one (and then mutate). Note that it does specifically mention immutable cannot be cast away and then modified (on that same page, see Removing Immutable With A Cast). It does not mention const, I assume that is on purpose. -Steve
Re: Rant after trying Rust a bit
On 7/23/2015 2:22 AM, Chris wrote: It's one thing to list nice features, and it's another thing to use these features in production code. As a code base grows, the limitations become more and more obvious. Thus, I would be wary of jumping to conclusions or hailing new features as game changers, before having tested them thoroughly in the real world. Only time will tell, if something really scales. I've learned to wait and see what people with experience report after a year or two of using a given language. It is very true that many features look good on paper, and only time and experience reveals the truth. There are a lot of programming features that fail the second clause - like implicit declaration of variables.
Re: Rant after trying Rust a bit
On Thursday, 23 July 2015 at 20:48:34 UTC, Ziad Hatahet wrote: The ternary operator becomes much harder to read the moment you have more than the simple if/else case. I think it is actually kinda pretty: auto x = (condition_1) ? 1 : (condition_2) ? 2 : (condition_3) ? 3 : 4; val x = { val ys = foo() ys.map(...).filter(...).exists(...) } auto x = { auto ys = foo(); return ys.map(...).filter(...).exists(...); }(); Before you get too worried about the (), I'd point out that this is a very common pattern in Javascript (for like everything...) and while everybody hates JS, most every uses it too; this patten is good enough for usefulness. (or for that you could prolly just write foo().map()... directly but i get your point)
Re: Silicon Valley D Meetup July 2015
On 07/23/2015 04:52 AM, Ali Çehreli wrote: http://www.meetup.com/D-Lang-Silicon-Valley/events/224003042/ Bonus item for the meeting: The first proof copy of Programming in D, which arrived today. You can touch and smell it if you come to the meeting! :p Ali
Re: Rant after trying Rust a bit
On Thu, Jul 23, 2015 at 2:00 PM, Adam D. Ruppe via Digitalmars-d digitalmars-d@puremagic.com wrote: I think it is actually kinda pretty: What about: int median(int a, int b, int c) { return (ab) ? (bc) ? b : (ac) ? c : a : (ac) ? a : (bc) ? c : b; } vs. def median(a: Int, b: Int, c: Int) = if (a b) { if (b c) b else if (a c) c else a } else if (a c) a else if (b c) c else b Before you get too worried about the (), I'd point out that this is a very common pattern in Javascript (for like everything...) and while everybody hates JS, most every uses it too; this patten is good enough for usefulness. Is the compiler always able to always optimize out the function call by inlining it, as would be the case with a scope?
Re: Rant after trying Rust a bit
On 7/23/15 5:07 AM, Walter Bright wrote: On 7/22/2015 11:47 PM, Jacob Carlborg wrote: On 2015-07-22 20:47, simendsjo wrote: Traits -- I think the ability to express an interface without buying into inheritance is the right move. The alternative in D is specifying the behavior as a template I completely agree and don't really like the approach D has implemented template constraints. Yeah I know, Andrei will destroy this :) Consider that template constraints can be arbitrarily complex, and can even check behavior, not just a list of function signatures ANDed together. Turns out many constraints in Phobos are of the form (A || B), not just (A B). Agreed. And that's just scratching the surface. Serious question: how do you express in Rust that a type implements one trait or another, then figure out statically which? and verifying the contract in a unittest for the type. I am a bit puzzled by the notion of shipping template code that has never been instantiated as being a positive thing. This has also turned up in the C++ static_if discussions. This is easy to understand. Weeding out uncovered code during compilation is a central feature of C++ concepts. Admitting you actually never want to do that would be a major blow. Andrei
Re: Rant after trying Rust a bit
On 2015-07-23 09:45, Joakim wrote: That's because it has its own top-level link in the sidebar, More libraries. You could argue that's not the best description of dub, but it's certainly prominently placed. Ah, right, I completely forgot about that. But one could expect the actual tool be available to download from dlang.org as well. -- /Jacob Carlborg
Re: Rant after trying Rust a bit
On 2015-07-23 12:56, Marc =?UTF-8?B?U2Now7x0eiI=?= schue...@gmx.net wrote: Expressions --- This probably also falls in the too late category, but statements-as-expressions is really nice. `auto a = if ...` - why not? Generally I find this an elegant concept. But in Rust, it leads to the distinction between expressions terminated with `;` and those without, which in turn makes it necessary to use braces even if you have only one statement or expression. This is something that I dislike very much. In Scala there's no problem. No semicolons are required a no braces. -- /Jacob Carlborg
Re: Converting uint[] slice to string for the purpose of hashing?
On Thursday, July 23, 2015 12:56:13 Temtaime via Digitalmars-d-learn wrote: All types are hashable and for your own structs and classes you can redefine opHash It's toHash, actually, but yeah. - Jonathan M Davis
Re: How to get *32mscoff libraries for phobos?
On Thursday, 23 July 2015 at 01:43:56 UTC, Mike Parker wrote: On Thursday, 23 July 2015 at 01:39:05 UTC, Mike Parker wrote: post at [1] where Rainer shared the relevant bits of a batch Gah, hate it when I forget the links. [1] http://forum.dlang.org/post/m456t5$2jc4$1...@digitalmars.com IT worked! Placing this Batch file in the dmd2\src Folder. -- BEGIN FILE: BUILD.bat set dm_make=C:\D\dmd2\windows\bin\make.exe set DMD=C:\D\dmd2\windows\bin\dmd.exe set cl32=C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin\cl.exe set ar32=C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin\lib.exe cd druntime del /q errno_c.obj complex.obj %dm_make% -f win64.mak DMD=%DMD% MODEL=32mscoff CC=\%cl32%\ if errorlevel 1 goto xit cd .. cd phobos cd etc\c\zlib %dm_make% -f win64.mak clean cd ..\..\.. %dm_make% -f win64.mak DMD=%DMD% MODEL=32mscoff CC=\%cl32%\ MAKE=%dm_make% AR=\%ar32%\ if errorlevel 1 goto xit cd .. -- END FILE I had to reinstall dmd (I think I messed up my dmd2\src folder somehow) I used visual studio 2013 community edition. SO. Where do i put it now that its built? I placed it in the dmd2\windows\lib and i still got LINK : fatal error LNK1104: cannot open file 'phobos32mscoff.lib' ... perhaps i need to edit the sc.ini file...
Re: Rant after trying Rust a bit
On Thursday, 23 July 2015 at 14:15:30 UTC, Andrei Alexandrescu wrote: On 7/23/15 5:07 AM, Walter Bright wrote: On 7/22/2015 11:47 PM, Jacob Carlborg wrote: On 2015-07-22 20:47, simendsjo wrote: Traits -- I think the ability to express an interface without buying into inheritance is the right move. The alternative in D is specifying the behavior as a template I completely agree and don't really like the approach D has implemented template constraints. Yeah I know, Andrei will destroy this :) Consider that template constraints can be arbitrarily complex, and can even check behavior, not just a list of function signatures ANDed together. Turns out many constraints in Phobos are of the form (A || B), not just (A B). Agreed. And that's just scratching the surface. It is definitely a big issue for designing more advanced generic libraries and one of my major issues with Rust but you need to realize that vast majority of application domain usage of such constraints is simply ensuring list of methods. You may be biased by too much standard library development ;) At the same time one HUGE deal breaker with rust traits that rarely gets mentioned is the fact that they are both constraints and interfaces at the same time: // this is template constraint, it will generate new `foo` symbol for each new T fn foo T : InputRange (range : T) // this use the very same trait definition but creates fat pointer on demand with simplistic dispatch table fn foo (range : InputRange) It kills all the necessity for hacks like RangeObject and is quite a salvation once you get to defining dynamic shared libraries with stable ABI. This is probably my most loved feature of Rust. Serious question: how do you express in Rust that a type implements one trait or another, then figure out statically which? As far as I understand current idiomatics, you don't. Code that tries to use functions that are not ensured by trait will simply not compile and for any complicated generic programming one is supposed to use macros. Rust does not have templates, only trait-restricted generics. I find it terribly unproductive but it seems to appeal to certain developer mindset, primarily the ones that associate templates with C++ templates.
Re: Rant after trying Rust a bit
On 7/23/15 6:56 AM, Marc =?UTF-8?B?U2Now7x0eiI=?= schue...@gmx.net wrote: Traits -- I think the ability to express an interface without buying into inheritance is the right move. The alternative in D is specifying the behavior as a template and verifying the contract in a unittest for the type. Not only that, but Rust does so much more with traits, e.g. closures. They seem to be a simple, yet powerful concept, that a big part of the language is built on. http://blog.rust-lang.org/2015/05/11/traits.html On the other hand, it is extremely restrictive in contrast to `static if()` and template constraints. D's approach is a lot more expressive and - at least for me - intuitive. Thanks for the link, good quick read to get the overview of Rust's traits feature. It's ingenious because it integrates static and dynamic dispatch. For dynamic dispatch, traits are better than interfaces - more flexible, better informed. For static dispatch, they don't hold a candle to D's constraints. This is important because dynamic dispatch is more of a cut-and-dried matter, whereas static dispatch is where it's at. For static dispatch I think D's template constraints are quite a lot better; they have a lot more power and offer a lot more to promise. They are an out-of-the-box solution that's a bit unwieldy because it's new enough to not yet have established idioms. In contrast, traits come from straight within the box. From a language perspective, it is my belief that Design by Introspection (enabled collectively by template constraints, introspection, compile-time function evaluation, and static if) is the one crushing advantage that D has over any competitor. Things like range-based algorithms are good but easy to copy and adapt. The faster we manage to get creative with Design by Introspection and describe it, systematize it, create compelling designs with it, the quicker will D win the minds and hearts of people. There's a lot at stake here. An question often asked by would-be users is What does D offer that's special? or What does D offer over language Xyz? As we all know there are many answers to that. Maybe too many. Now I know what the answer is: Design by Introspection. Andrei
Re: Rant after trying Rust a bit
On 2015-07-23 11:07, Walter Bright wrote: Consider that template constraints can be arbitrarily complex, and can even check behavior, not just a list of function signatures ANDed together. Turns out many constraints in Phobos are of the form (A || B), not just (A B). I know that is possible, but in most cases it's only a list of function signatures that is needed. -- /Jacob Carlborg
Re: Wait, what? What is AliasSeq?
On 07/23/2015 10:42 AM, deadalnix wrote: On Wednesday, 22 July 2015 at 04:44:48 UTC, Timon Gehr wrote: On 07/22/2015 12:53 AM, deadalnix wrote: On Tuesday, 21 July 2015 at 21:26:24 UTC, Daniel N wrote: On Tuesday, 21 July 2015 at 16:54:54 UTC, H. S. Teoh wrote: Because, among other things, it auto-expands. T 1) .tupleof auto-expands and changing it at this point would cause epic breakage.(I also see no reason to.) This is actually a very good point. First of, tupleof does not return a TypeTuple, but they have something similar in nature. What's the difference? There shouldn't be any. You can't put runtime values into the first one, you can into the second one. It should be just a bunch of aliases to the field members, which have runtime values. Anyway, I figured out the difference: alias Seq(T...)=T; void main(){ struct S{ int x,y,z; } S s; alias a=s.tupleof; // error alias b=Seq!(s.x,s.y,s.z); // ok alias c=Seq!(s.tupleof); // ok typeof(s.tupleof) x; alias d=x; // ok } Wtf.
Re: Rant after trying Rust a bit
On Thursday, 23 July 2015 at 13:30:49 UTC, Andrei Alexandrescu wrote: At the end of the day everything-is-an-expression is natural for functional languages, but doesn't seem it makes a large difference to an imperative language. Good points.
Re: Experience upgrading template code from 2.065 to 2.067
On Thursday, 23 July 2015 at 13:20:32 UTC, Nicholas Wilson wrote: On Wednesday, 22 July 2015 at 15:50:13 UTC, simendsjo wrote: // 1 template Tmlp(T) {} // 2 template Tmpl(alias T) {} I'm assuming thats a typo Yes. I just wrote the code in the post without trying to compile it. I'm not as good at caching typos as the compiler.
Re: Sending an immutable object to a thread
On Thursday, 23 July 2015 at 09:05:12 UTC, Marc Schütz wrote: It is not safe, but for a different reason: `mt` is already a _reference_ to the actual object (that's how classes behave in D). This reference is located in a register or on the stack, and `mt` is therefore a pointer into the stack. It's illegal to return that pointer from the function, because it will become invalid once the function is left. Fortunately, the compiler can detect simple cases like this one, and will refuse to compile it: Object* foo() { Object o; return o; } xx.d(3): Error: escaping reference to local o Very interesting. You see, I am trying to resolve the distinction be a value type and a reference type in D. If Object were declared as a struct, this would make sense to me. The object would be created on the stack as a temporary, and it would disappear when the function exited. So returning a pointer to it would be a very, very bad thing. But a class object is allocated in the GC heap. I would have guessed that, since a class reference is just essentially a hidden pointer, that the address-of operator '' for a class object would return the address into the heap... not the address of the reference itself! Just a little syntactic sugar. But that's not the case. I thought this was true: class MyThing { ... }; MyThing a = new MyThing, b = a; assert(a == b); // Fails In a weird way, that makes total sense to me, and no sense at all. So, passing a pointer to a stack-based reference from one thread is another is not necessarily a good thing to do, as the original reference might disappear while the thread is using it. Is there a way to get the address of the actual heap object from a class reference? Or am I drifting to far into the realm of unsafe. This all goes back to my original question of passing an immutable object from one thread to another. It is simple with arrays, since there is a clear distinction between the array reference and its contents. You can easily create a mutable reference to immutable contents with an array. But it seems rather convoluted with class objects. So, in the end, it seems best to send a rebindable reference to the other thread, and perhaps hide the mild ugliness behind a library API that takes an immutable object and then sends the rebindable version, like: void send_message(Tid tid, immutable(Message) msg) { send(tid, thisTid(), rebindable(msg)); } That seems easy enough. Thanks much for all the help.
Re: Rant after trying Rust a bit
On Thursday, 23 July 2015 at 13:33:43 UTC, Adam D. Ruppe wrote: On Wednesday, 22 July 2015 at 21:04:57 UTC, simendsjo wrote: :) The example was written to save space. I recon you understand what I mean. Yeah, but the if/else is one of the most useful examples of it, and is covered by ?:, so the whole thing becomes less compelling then. The other places where I've used it in languages that support it are little blocks crammed into a line and sometimes exception grabbing... but still, the value isn't that great. If we had a clean sheet wouldn't it be better to have if return a value and ditch ternary?
Re: C bindings: typedef struct conflicts with method
On 2015-07-23 03:57, Mike Parker wrote: In your case, rd_kafka_metadata is the name of the struct, but in C instances would need to be declared like so: struct rd_kafka_metadata instance; Since the struct is declared directly in the typedef, is the struct name actually available? -- /Jacob Carlborg
Re: New D book available for pre-order: D Web Development
On Wednesday, 22 July 2015 at 21:43:09 UTC, notna wrote: On Wednesday, 22 July 2015 at 21:19:20 UTC, notna wrote: On Wednesday, 22 July 2015 at 15:29:20 UTC, Kai Nacke wrote: As I have all the books/hardcopies from http://wiki.dlang.org/Books, Added you book to the Wiki. Hope you and your publisher don't mind ;) Thank you very much! Regards, Kai
Re: Learning D Available for Pre-Order
On Tuesday, 23 June 2015 at 14:47:03 UTC, Mike Parker wrote: The project that has taken me away from Derelict since the end of February is now available for pre-order at [1]. I'm [1] https://www.packtpub.com/application-development/learning-d Great work, for sure I'll buy a copy! I missed your book on http://wiki.dlang.org/Books and added it. Regards, Kai
Re: New D book available for pre-order: D Web Development
On Wednesday, 22 July 2015 at 15:46:09 UTC, Sönke Ludwig wrote: Nice! I had heard of the plans for this book, but didn't have time to actively contribute. But give me a ping if you need some additional review. I can't promise that I'll be fast, but I'll do my best! Great offer! I'll contact you. Thanks! Regards, Kai
Re: Rant after trying Rust a bit
On Wednesday, 22 July 2015 at 23:25:56 UTC, jmh530 wrote: If traits were deemed important enough to add to D, I would suggest just extending interfaces so that they have inheritance and can be easily used in template constraints. I think I had a few misconception about D interfaces. The documentation does not say anything about interface inheritance, but it seems to work when I tried it. It definitely seems weird that this behavior isn't mentioned anywhere. Also, the use of static* or final methods in the interface can allow default implementations of methods. This is something that can be done with Scala traits. I feel like this isn't documentated as well as it could be. A weirder thing is that when I tried to test that static and final methods could not overridden (which is allowed in Scala), it seemed like they were getting overridden. The tricky part is that I had been using something like final foo() { } instead of final void foo() { }. For some reason you can override the first, but not the second. So really, I guess the D interfaces are more powerful than I had thought. Only downside is that they only work for classes. * I feel like these also aren't particularly described well in the documentation either.
Re: Silicon Valley D Meetup July 2015
On 7/23/15 7:52 AM, Ali Çehreli wrote: Thanks to Chris Jacobi, we may have a permanent venue: Hacker Dojo in Mountain View: http://www.meetup.com/D-Lang-Silicon-Valley/events/224003042/ This will be a social get together with an open agenda. Bring your lightning talk and any other topic with or without a slide deck. Ali https://twitter.com/D_Programming/status/624229374603169792 https://www.facebook.com/dlang.org/posts/1107571949256485 I encourage everyone in the community to boost such announcements to social outlets like Twitter and Facebook. Andrei
Re: Rant after trying Rust a bit
On Thursday, 23 July 2015 at 13:30:49 UTC, Andrei Alexandrescu wrote: I used to be quite jazzed about the everything-is-an-expression mantra, but it's not all great. 1. Inferring function return types when everything is an expression (i.e. last expression there is the return type) may yield WAT results. 2. Defining a result type for loops is awkward. At the end of the day everything-is-an-expression is natural for functional languages, but doesn't seem it makes a large difference to an imperative language. It also works well for Ruby with its dynamic typing. Function return types don't matter, of course, and for loops you just use whatever the last executed expression happens to be. Anyway, in D we have delegate literals, for the rare cases where it's useful.
Re: Rant after trying Rust a bit
On Thursday, 23 July 2015 at 15:37:24 UTC, shannon mackey wrote: On Wednesday, 22 July 2015 at 19:41:16 UTC, Jack Stouffer wrote: On Wednesday, 22 July 2015 at 20:43:04 UTC, simendsjo wrote: On Wednesday, 22 July 2015 at 18:47:33 UTC, simendsjo wrote: [...] Don't quite know what you mean here. When everything is an expressions, you can write things like auto a = if(e) c else d; In D you have to write type a = invalid_value; if(e) a = c; else a = d; assert(a != invalid_value); I frequently do things like this in D: auto good = ( true == true ) ? true : false; which looks a lot like your Rust example? Is that not what you were looking for? Sorry, I didn't read to the end of the thread, and this has been covered many times.
Re: Rant after trying Rust a bit
On Thursday, 23 July 2015 at 14:49:55 UTC, ixid wrote: On Thursday, 23 July 2015 at 13:33:43 UTC, Adam D. Ruppe wrote: On Wednesday, 22 July 2015 at 21:04:57 UTC, simendsjo wrote: :) The example was written to save space. I recon you understand what I mean. Yeah, but the if/else is one of the most useful examples of it, and is covered by ?:, so the whole thing becomes less compelling then. The other places where I've used it in languages that support it are little blocks crammed into a line and sometimes exception grabbing... but still, the value isn't that great. If we had a clean sheet wouldn't it be better to have if return a value and ditch ternary? Maybe, but the ternary operator is a lot less verbose, and from some other comments in this thread, it sounds like the way they implemented it in Rust forces you to use braces for single line statements, which would be a _huge_ downside IMHO. I'm inclined to think that it would need a use case that's a lot more compelling than if-else chains to be worth it. - Jonathan M Davis
Re: Wait, what? What is AliasSeq?
On Thursday, 23 July 2015 at 15:03:24 UTC, Timon Gehr wrote: On 07/23/2015 10:42 AM, deadalnix wrote: On Wednesday, 22 July 2015 at 04:44:48 UTC, Timon Gehr wrote: On 07/22/2015 12:53 AM, deadalnix wrote: On Tuesday, 21 July 2015 at 21:26:24 UTC, Daniel N wrote: On Tuesday, 21 July 2015 at 16:54:54 UTC, H. S. Teoh wrote: Because, among other things, it auto-expands. T 1) .tupleof auto-expands and changing it at this point would cause epic breakage.(I also see no reason to.) This is actually a very good point. First of, tupleof does not return a TypeTuple, but they have something similar in nature. What's the difference? There shouldn't be any. You can't put runtime values into the first one, you can into the second one. It should be just a bunch of aliases to the field members, which have runtime values. Anyway, I figured out the difference: alias Seq(T...)=T; void main(){ struct S{ int x,y,z; } S s; alias a=s.tupleof; // error alias b=Seq!(s.x,s.y,s.z); // ok alias c=Seq!(s.tupleof); // ok typeof(s.tupleof) x; alias d=x; // ok } Wtf. That definitely makes it seem like they _should_ be the same, though they clearly aren't. That may just be part of the issues related to alias not being consistent that David was bringing up before though. Regardless, I'm inclined to agree that what tupleof gives you should just be a TypeTuple/AliasSeq/whatever, though it would be nice to rename it once we actually settle on a name for the library construct. - Jonathan M Davis