Re: duck!
Andrei Alexandrescu wrote: I think it's best to concede and call the artifact "adapt". Aww. I liked duck. -- Simen
Re: [due diligence] std.xml
Michel Fortin wrote: If someone wants to proceed I'll extract the code from the rest of my code and release it under the boost license. I'd love to give this a spin. -- Simen
Re: [due diligence] std.xml
Michel Fortin wrote: On 2010-10-19 21:31:04 -0400, Michel Fortin said: On 2010-10-19 19:28:18 -0400, "Simen kjaeraas" said: Michel Fortin wrote: If someone wants to proceed I'll extract the code from the rest of my code and release it under the boost license. I'd love to give this a spin. Great. I'll post that code tomorrow. Not yet tomorrow, but it's ready. Have fun. <http://michelf.com/docs/d/mfr-xml-2010-10-19.zip> I've included some notes in the archive about what each module do and what's missing. Feel free to ask if you have questions. Thank you. I'll have a look-see. -- Simen
Re: [debate] About D's pretension for homoiconicicity
Justin Johansson wrote: The formal subject proposed for debate is "D's advanced meta-programming capabilities essentially makes it (D) as if it were a homoiconic language, and brings with it all the advantages of homoiconic languages." Hardly true. While it may be possible to Greenspun together a hacky attempt at homoiconicity in D, Doing so would bring but a few of the benefits compared to a language designed from the beginning with homoiconicity in mind. That said, some of the use cases for homoiconicity may be covered by D's capabilities, and this may be closer to what you intended by the above paragraph. Certainly D does not allow one to execute arbitrary strings as if they were D code (D does however allow the execution of arbitrary strings). There certainly are ways to do that, but such could be said for any language. It would certainly be possible to create a system for manipulating ASTs at compile time in D, be they generated by library code or the compiler itself. Converting such a representation to a mixin string should prove no big challenge. However, even such a system would likely lack the elegance of lisp's macros, and I find it unlikely it would find much use. Another problem with this is that it's useless at runtime, unless an interface to the compiler is offered as a library for D. This has been discussed numerous times, and I believe most on this list agree it would a Good Thing™. -- Simen
Re: The Next Big Language
Paulo Pinto wrote: Sorry but I still don't get it. Do you mean that the types that erased and the same code is generated? Then let me say that .Net generics get generated on the fly and JITed for each different type. Eiffel and Modula-3 generics also have specific generated code for each type. The major difference regarding C++ code is that the linkers are smarter and are able to remove duplicates of the generated code for the same set of type arguments. The point is, D and C++ templates can do a lot more than simple generics. Generics is limited to simple copying and pasting of type names, whereas templates can build a DSL parser for you, let you choose whether a function should be parallelized, a type thread-safe, etc. -- Simen
Re: How to work with an "arbitrary input range"?
Adam D. Ruppe wrote: [snip] Anyway, what's the right thing to do here? I'd say: void fun( T )( T t ) if ( isInputRange!T && is( ElementType!T == dchar ) ) {} -- Simen
Re: How to work with an "arbitrary input range"?
Jonathan M Davis wrote: void fun( T )( T t ) if ( isInputRange!T && is( ElementType!T == dchar ) ) {} Though I'd suggest using Unqual!(ElementType!T). You're right, of course. -- Simen
Re: Q: What are the rules for emitting template code?
Austin Hastings wrote: If Decider uses Option1.sizeof, does any Option1 code get emitted? For sizeof to be known, the template would have to be instantiated, yes. If Decider uses some external function that makes use of type aliases in Option1, (example: Option1() { alias byte value_t; } ) does any Option1 code get emitted? Again, the same. If Decider uses some function defined inside the same module with Option1, but NOT inside of Option1, does any/all of the Option1 code get emitted? Accessing the surrounding scope would not instantiate Option1. So no. If Decider uses a static method of Option1, does any more of the Option1 code get emitted? As with the first two, it would have to be instantiated. Now, I have the feeling this is not really what you're asking for. What do you mean by 'code gets emitted'? Perhaps you could give a reduced example? -- Simen
Re: Improving std.range.Zip
On Sun, 24 Oct 2010 21:39:24 +0200, bearophile wrote: Tomek S.: map!((a) {return myNaryFun(a._0, a._1, ...); })(zip(range1, range2, ...)); Currently the docs of std.algorithm.map say: Multiple functions can be passed to map. In that case, the element type of map is a tuple containing one element for each function.< But lot of time ago I have said that in my opinion that's not the best design. Python has a different design, its map does what you want, you may write your code in Python as: map(myNaryFun, range1, range2, ...) An example (Python 2.6): a = ["a", "b", "c", "d"] b = [1, 2, 3, 4] map(lambda c,n: c * n, a, b) ['a', 'bb', 'ccc', ''] Is is possible to change the std.algorithm.map to a semantics similar to the Python one, that I think is more useful? From what I can see, map currently simply doesn't support passing it multiple ranges. It would be a trivial change to let it support multiple ranges in addition to multiple functions. -- Simen
Re: Simulating Multiple Inheritance
dsimcha wrote: I've just found a new D-specific design pattern for simulating non-virtual multiple inheritance by abusing inner classes and alias this. Here's an example: class Foo { uint num = 2; } class Bar { uint num = 3; } class Outer : Foo { class Inner : Bar {} Inner inner; this() { inner = new Inner; } alias inner this; } I believe this is mentioned in TDPL. -- Simen
Re: Simulating Multiple Inheritance
Jimmy Cao wrote: Well, you can't have multiple alias this's right? So then, wouldn't it still only limit to 2 base classes? It is extensible like this: class Outer : Foo { class Inner : Bar { class InnerInner : Baz { } InnerInner innerInner; this() { innerInner = new InnerInner; } alias innerInner this; } Inner inner; this() { inner = new Inner; } alias inner this; } It should be possible to turn this into a mixin. -- Simen
Re: Simulating Multiple Inheritance
Simen kjaeraas wrote: It should be possible to turn this into a mixin. Problem is of course, no reliable way to hijack constructors to initialize the inner class. -- Simen
Re: GCC 4.6
Nick Sabalausky wrote: I can certainly understand the impossibility of actually keeping up with bearophile, but I don't think that even he expects that every idea he brings up be rushed into D. Yeah, I always see bearophile's lists as 'maybe some of this could fit in D at some point'. Well worth having on the list, but not to be regarded as The Bearer of Absolute Commandments. That said, I fully understand that Walter could get more tired of it than do I - I don't feel the pressure. -- Simen
Re: Ruling out arbitrary cost copy construction?
Andrei Alexandrescu wrote: Walter and I discussed the matter again today and we're on the brink of deciding that cheap copy construction is to be assumed. This simplifies the language and the library a great deal, and makes it perfectly good for 95% of the cases. For a minority of types, code would need to go through extra hoops (e.g. COW, refcounting) to be compliant. I'm looking for more feedback from the larger D community. This is a very important decision that marks one of the largest departures from the C++ style. Taking the wrong turn here could alienate many programmers coming from C++. So, everybody - this is really the time to speak up or forever be silent. I am almost certain you're doing the right thing by assuming cheap copy construction, but I have no good arguments to back me up. Hence my holding my breath and waiting, thus far. -- Simen
Re: The Expressiveness of D
]Gary Whatmore wrote: The D way of returning tuples is: T!(int,int) ret; auto f = (ref T!(int,int) r){ r = e; }; f(ret); D also has better tuples in std.typecons. auto t1 = tuple( 1,2 ); auto t2 = { return t1; } -- Simen
Re: The Expressiveness of D
%u wrote: class A {} class B : A {} class C : A {} template T(A...) { alias A T; } void main() { auto a = true ? new B : new C; // these don't work - why? // auto b = [new B, new C]; // auto c = { return [1: new B,2: new C]; }; "The type of the first element is taken to be the type of all the elements, and all elements are implicitly converted to that type." (http://digitalmars.com/d/2.0/expression.html#ArrayLiteral) I believe it has been discussed numerous times before that the ?: test should be used to find the element type - not sure why it isn't. T!(int,int) e = (1,2); e = T!(3,4); // ah - so (1,2) syntax on initialization, T!(1,2) when assigning! T!(int,int) d = T!(1,2); e = d; // tuples aren't first class, why? // auto f = { return e; }; } Compile-time argument tuples are something of a strange beast. They behave not very unlike tuples, but due to their ability to hold types, literals, expressions and aliases to whatever, they are not a very good match for what you'd expect tuples to be. (e.g, what do you expect T!(3,T) to be?) For tuples, you should instead look into std.typecons' Tuple struct and tuple function: Tuple!( int, "n", string, "s" ) tup; tup.n = 4; tup.s = "A string! My kingdom for a string!"; auto tup2 = tuple( 1, 2 ); assert( is( typeof( tup2 ) == Tuple!( int, int ) ) ); For even better support of tuples, you should have a look-see at Philippe Sigaud's dranges.tuple and dranges.reftuple (http://svn.dsource.org/projects/dranges/trunk/dranges/docs/tuple.html and http://svn.dsource.org/projects/dranges/trunk/dranges/docs/reftuple.html) The latter is absolutely awesome, and should be made part of phobos ASAP, IMO (though according to the documentation, it is not in tip-top shape). Examples from the reftuple page: int a, b; _(a,b) = tuple(b,a); // swap _(a,b) = tuple(b,a+b); // fibonacci int[] arr = [0,1,2,3,4]; int [] c; _(a,b,c) = arr; // a = 0, b = 1, c = [2,3,4] -- Simen
Re: The Expressiveness of D
Andrei Alexandrescu wrote: I believe it has been discussed numerous times before that the ?: test should be used to find the element type - not sure why it isn't. Looks like outdated documentation to me. Well, it certainly doesn't behave as it should, if %u's post is anything to go by. (and my testing of the same issue - it's not just %u) Filed as 5156. This was already contributed to in Phobos (under a different syntax) but I rejected it on grounds of safety: the value built by "_" must escape addresses of all of its arguments, so it's not safe. True. Bummer. Hm, now, the intent of _ is that the constructed type should exist only as a temporary, I believe. Maybe it is possible to do this in a safer way. I actually have a solution, which I have posted here before, and I feel might be worthy of an enhancement request: that templates may be used as pseudo-types, in that a operator overloading should be allowed on non-type template instances: import std.typecons; import std.typetuple; import dranges.templates; import dranges.typetuple; template _( T... ) if ( allSatisfy!( isAlias, T ) ) { Tuple!( StaticMap!( TypeOf, T ) ) opAssign( Tuple!( StaticMap!( TypeOf, T ) ) args ) { foreach ( i, e; T ) { e = args[i]; } return args; } } unittest { int a = 1; b = 1; _!( a, b ) = tuple( b, a+b ); // Equivalent to _!( a, b ).opAssign( b, a+b ); } Sadly, this currently does not work, not just due to the aforementioned limitation, but also because the reassigned values are discarded as the function returns. This latter part is confusing me. Is this intended behavior? A different solution might be @ephemeral, marking types that are not supposed to be stored anywhere, and statically disallowing their use as variables. My suggestion to address this issue was (and is) to make the assigned part of the function call: int a, b; scatter(tuple(b, a), a, b); scatter(tuple(b, a + b), a, b); int[] arr = [0, 1, 2, 3, 4]; int[] c; scatter(arr, a, b, c); That certainly works, but it is not as prettiful or straightforward. -- Simen
Re: The Expressiveness of D
Lutger wrote: It is stated in TDPL that the ?: test should be used, and it already works: auto a = [1,2,3.5]; pragma(msg, typeof(a).stringof); // prints double[], not int[] So it works for things that are not classes, then. That, to me, counts as 'does not work correctly'. Filed as 5156. -- Simen
Re: The Expressiveness of D
Simen kjaeraas wrote: Sadly, this currently does not work, not just due to the aforementioned limitation, but also because the reassigned values are discarded as the function returns. This latter part is confusing me. Is this intended behavior? Curiouser: T[i] = args[i]; works. -- Simen
Re: @disable on override function
Jesse Phillips wrote: Should the compiler not compile the class saying something to the effect of: Can not disable method hello in base class A from B. I think this is the best solution. "Cannot @disable overriding functions", I'd say. -- Simen
Re: Spec#, nullables and more
Walter Bright wrote: Adam D. Ruppe wrote: A non-nullable type is basically just: struct NotNull(T) { T _payload; alias _payload this; invariant() { assert(_payload !is null); } } If we could disable the default constructor, this approach might just work. Sure, the checks here would be at runtime, but you'd have them without having to manually write the assert each time. All that does is reinvent the null pointer seg fault. The hardware does this for you for free. A null pointer is what's known as a "white hole", all attempts to access the object are rejected. I believe you managed to miss the important part, "If we could disable the default constructor". The invariant here is not about using the NotNull!T, but for when you assign to it. In other words: struct NotNull( T ) if ( is( T == class ) || isPointer!T ) { private T _payload; alias _payload this; this( T data ) { assert( data !is null ); _payload = data; } @disable this( ); @disable void opAssign( T ); } NotNull!T notNull( T )( T ptr ) { return NotNull!T( ptr ); } It might not cover all bases, but the idea is it is impossible to create a NotNull!T from a null T, and impossible to assign a null T to one. -- Simen
Re: Spec#, nullables and more
Walter Bright wrote: Consider non-nullable type T: T[] a = new T[4]; As others have pointed out, this would be impossible for a proper non-nullable type. The only ways to create an array of non-nullable elements would be a literal, concatenation with non-nullable elements, or casting an existing array of nullable elements. You do not have write access to the length of an array of non-nullable elements, so that you cannot increase the length (thus adding null elements). You can decrease the length by slicing. This way, it should be impossible to have elements of our array that are null. -- Simen
Re: Spec#, nullables and more
Walter Bright wrote: Adam D. Ruppe wrote: It wasn't until I added the invariant and in/out contracts to all the functions asserting about null that the problem's true cause became apparent. Couldn't this happen to you with any datum that has an unexpected value in it? Yes. And by augmenting the type system (such as with non-nullable pointers or bounded integers), such a mistake could be caught early, likely at compile time, because the bounded type or non-null pointer does not allow assignment from arbitrary pointers or values. Suppose, for example, you are appending the numbers 1..5 to the array, and somehow appended a 17. Many moons later, something crashes because the 17 was out of range. Bounded!(int, 1, 5)[] myArr; myArr ~= 1; // Compile-time error: int is not implicitly castable to Bounded!(int, 1, 5) myArr ~= Bounded!(int, 1, 5)( 1 ); // Works perfectly -- Simen
Re: Spec#, nullables and more
bearophile wrote: Simen kjaeraas: Bounded!(int, 1, 5)[] myArr; myArr ~= 1; // Compile-time error: int is not implicitly castable to Bounded!(int, 1, 5) myArr ~= Bounded!(int, 1, 5)( 1 ); // Works perfectly I can't say this is a nice syntax :-) (Isn't the implicit conversion + bound test better?) Not sure. This way is more explicit, and errors will be caught at compile-time. -- Simen
Re: Spec#, nullables and more
bearophile wrote: Simen kjaeraas: Not sure. This way is more explicit, and errors will be caught at compile-time. I see. But if a syntax is ugly and too much heavy, people (rightly) don't use it... (This is why bounded values are good as builtins). Of course. Now, aliases help a bit here, turning that into something like: alias Bounded!(int, 1, 5) myInt; myInt[] myArr; myArr ~= myInt( 1 ); I believe using an alias would be a good idea in most such cases, as one's bound to write Bounded!(int,1,6) in a long program, and wonder why it doesn't compile. -- Simen
Re: Spec#, nullables and more
Simen kjaeraas wrote: bearophile wrote: Simen kjaeraas: Not sure. This way is more explicit, and errors will be caught at compile-time. I see. But if a syntax is ugly and too much heavy, people (rightly) don't use it... (This is why bounded values are good as builtins). Of course. Now, aliases help a bit here, turning that into something like: alias Bounded!(int, 1, 5) myInt; myInt[] myArr; myArr ~= myInt( 1 ); I believe using an alias would be a good idea in most such cases, as one's bound to write Bounded!(int,1,6) in a long program, and wonder why it doesn't compile. Worth adding too: myInt(648492) is of course not catchable at compile time, while such would be easy with builtin bounded integers. -- Simen
Re: null [re: spec#]
steveh wrote: I've known there are hidden tradeoffs when using these 'intelligent' types. Non-null types add terrible runtime checks everywhere. It's simply not acceptable in a systems programming language. Operating system kernel uses very efficient uninitialized arrays for buffers. It's crazy to force this non-null hype down our throats. And here you are sorely mistaken. The point of non-nullable types is the exact opposite. In fact, the only time you need a runtime check is when converting a nullable type to a non-nullable - after that, it's guaranteed not to be null. When in addition there is no way to create a non-nullable variable without initializing it (and possibly checking that the initializing value is not null, if it comes from a nullable source), the result is a great decrease in runtime checks, not an increase. -- Simen
Re: Should pure functions be prevented from reading changeable immutable static variables?
Don wrote: Pure functions are allowed to read immutable global variables. Currently, this even includes globals which are initialized from inside 'static this()'. Here's an example of how this can be a problem: immutable int unstable; pure int buggy() { return unstable; } static this() { // fails even though buggy is pure assert( buggy() == ( ++unstable , buggy() ) ); } I suspect that such functions should be forbidden from being 'pure'. Note that they cannot be used in CTFE (conceptually, all other @safe pure functions could be used in CTFE, even though the current implementation doesn't always allow it). The motivation for wanting to ban them is to prevent the optimiser from generating bad code. But if they were disallowed, there would also be benefits for breaking circular module dependencies: * if module A imports only pure functions from module B, we know that the static this() of A does not directly depend on the static this() of module B. (Note though it might still depend on C, which depends on B). * if the static this() of module A only calls pure functions, it does not depend on the static this() of any other module. Probably, this would only avoid the most trivial circular module dependencies, so not a huge win, but still a nice bonus. This feels a bit wrong to me - the only time when that needs to be disallowed is in static constructors, so it seems like an edge case. As you say, there are benefits to this solution, but I'm not sure they outweigh the disadvantages. It all comes down to a case of 'I feel' vs. 'you feel', though. -- Simen
Re: Spec#, nullables and more
Gary Whatmore wrote: non-nulls don't solve basic problems like arrays, basic collections in the library (custom fill policy). As I've posted elsewhere, arrays (and other collections) will have to work slightly differently for non-nullable elements: 1 .length is read-only (or read/decrease only) 2 Length can be increased by concatenation only 3 Can only be created with a nonzero length with a runtime check of a nullable-element array/collection, or as a literal containing only non-nullable elements. And yes, there may be cases where this is not good enough, and you need to have nullable elements. That does not mean non-nullable types are borked by default, you just use nullable elements, and accept that you have to work harder to prevent segfaults. In many cases, rule #3 will allow you to create your collection using nullable elements, and casting it to non-nullable afterwards. Can't force me to read. I've made my choice of using D already. Nothing will change that. He's not trying to make you change language, but to inform you of how other languages have solved the problem. -- Simen
Re: Spec#, nullables and more
tls wrote: I never seen segfault in practice. Have you? Have you ever touched a computer? I see segfaults about daily. In my own code, not that often. -- Simen
Re: Spec#, nullables and more
Walter Bright wrote: I don't see that non-null is such a special case that it would benefit from a special case syntax. For example: There are some things that set non-null apart: 1. Few operations change the value of a pointer/class reference: assignment pointer arithmetic ref passing Of these, the last two can be disallowed in most cases. For e.g. an int, operator overloading adds bunches of more cases, and for structs/ classes where fields factor into the calculation, every single public member function and field would need to be wrapped. Simply put, null pointers are low-hanging fruit. 2. There is a single invalid value This is a simplification, of course, as you could argue that a reference to an A is not a valid reference to a B, and pointers may point to arbitrary areas of memory, where nothing of interest may be found. Again, low-hanging fruit. The value 25 requires far more context for the compiler to identify it as a bad value and determine how the developer should handle it. The compiler wouldn't do it. It would be a user supplied filter for a user defined subtype. Definitely a possibility, also for non-nullable. However, for such a system to be foolproof (and that's what we want), we need a way to disable the default constructor for structs. There is also the problem of arrays, as I expounded on in a different post. Without language support, this will be a hole in the system. -- Simen
Re: D Best Practices: Default initializers for structs
d'oh wrote: Arguable floating points would do better with -Infinity Why? A signaling nan tells you 'you'trying to use an uninitialized value!'. and integral types with a negative value e.g. -1, -2 or as negative as possible. Why? I can see some reasons, like foo * -1 yields a negative answer, while foo * 0 yields 0, which is a fairly common value. Still what are your reasons for saying it's the best? I suspect booleans would do better with true (this being the furtherest away from false .. that if is false is taken to be the origin of the boolean value axis). And if true is taken to be the origin, then false would be the best? -- Simen
Re: Can non-nullable references be implemented as a library?
Denis Koroskin <2kor...@gmail.com> wrote: Since many people think that non-nullable references can be implemented as a library and thus don't belong to core language, I've decided to show that it is in fact impossible to do so. How do you enforce the following behavior: [snip] Without support of these use-cases NonNull!(T) is useless. There can be other examples, but I think these are enough to prove that non-nullable references can not be implemented in library. Indeed. There is also the arrays I've mentioned before, and as you mention here, fields of classes and structs. Good catch. -- Simen
Re: null [re: spec#]
Roman Ivanov wrote: I know what your mean, but the example is flawed: public void foo() { if (m) { Object p = new Object(); p.toString(); } } You misunderstand. The idea is this: void foo( ) { Object p; if ( m ) { p = new Object( ); p.DoSomethingThatNeedsToBeDoneNow( ); } // 20 lines of code here if ( m ) { p.doSomethingWeird( dataFromAbove ); } } int x = { if (s == Dri.one) return 1; else if (s == Dri.two) return 2; else return 3; }(); This works. I'm not sure how efficient it is, but that would solve the issue with initialization without any path analysis. I'm not sure the compiler actually does this, but it would be fairly trivial to inline that delegate. -- Simen
Re: Can non-nullable references be implemented as a library?
Simen kjaeraas wrote: Denis Koroskin <2kor...@gmail.com> wrote: Since many people think that non-nullable references can be implemented as a library and thus don't belong to core language, I've decided to show that it is in fact impossible to do so. How do you enforce the following behavior: [snip] Without support of these use-cases NonNull!(T) is useless. There can be other examples, but I think these are enough to prove that non-nullable references can not be implemented in library. Indeed. There is also the arrays I've mentioned before, and as you mention here, fields of classes and structs. Good catch. Worth adding: Even if non-null references/pointers cannot be perfectly implemented in a library, they may still be a worthwhile addition to Phobos, to mark function arguments and the like. -- Simen
Re: null [re: spec#]
Nick Sabalausky wrote: If you do that, then there's two possibilities: A. You intended p to get inited on all code paths but forgot a codepath. With real init-checking the compiler will tell you and you can fix it. With D as it is, you're not informed at all, and you may or may not catch the problem before deployment. Obviously the former is better. B. You *intended* p to not always be inited, in which case the correct code is this: void foo( ) { Object p=null; if ( m ) { p = new Object( ); p.DoSomethingThatNeedsToBeDoneNow( ); } // 20 lines of code here if ( p != null ) { p.doSomethingWeird( dataFromAbove ); } } There is a third option, wherein the if condition will only be true if p !is null, but the actual condition is more complex: if ( m > 4 ) { p = new Object( ); p.DoSomethingThatNeedsToBeDoneNow( ); } // code if ( m > 12 ) { p.doSomethingWeird( dataFromAbove ); } -- Simen
Re: null [re: spec#]
Nick Sabalausky wrote: - if ( m > 12 ) { + if ( p && m > 12 ) { And you can toss in an "if(m>12) assert(p);" if you're worried about that. Of course. But the point is, this is unnecessary. We know p !is null when m > 4. -- Simen
Re: null [re: spec#]
Eric Poggel wrote: On 11/6/2010 6:50 AM, bearophile wrote: foobar: Any type can be wrapped by an OPTION type. trying to do the converse of this is impractical and is bad design. Discussing this is a waste of time now, this part of the D language will probably never change. This is why other people and me are proposition something different. Bye, bearophile I still live in D1 land, so forgive me if I'm out of the loop--but what keeps this from being implemented at the library level as templated type: NotNull!(T) ? If there are limitations, maybe these areas of the language can be improved to get us there? NotNull!T needs to have its default constructor disabled. That is the #1 blocker. There are other problems, mostly related to this - classes and structs with NotNull!T fields must define a constructor, and for structs that means the default destructor either needs to be possible to define or possible to disable. Arrays of NotNull!T may not be resized to a greater length, as that would require calling the default constructor of NotNull!T. I think that's it. -- Simen
Re: null [re: spec#]
Andrei Alexandrescu wrote: On 11/7/10 4:21 PM, bearophile wrote: Andrei Alexandrescu: And what was exactly the claim that was wrong? That there is no need of compiler& syntax support to implement "good enough" nonnullable reference types in D. The only change needed is constructor flow to make sure types with @disable'd constructors are always properly initialized. Array construction can be supported with a library function. One more thing: structs with NotNull!T fields will need to have their default constructor disabled automagically. -- Simen
Re: null [re: spec#]
Denis Koroskin <2kor...@gmail.com> wrote: On Mon, 08 Nov 2010 12:08:01 +0300, Simen kjaeraas wrote: Eric Poggel wrote: On 11/6/2010 6:50 AM, bearophile wrote: foobar: Any type can be wrapped by an OPTION type. trying to do the converse of this is impractical and is bad design. Discussing this is a waste of time now, this part of the D language will probably never change. This is why other people and me are proposition something different. Bye, bearophile I still live in D1 land, so forgive me if I'm out of the loop--but what keeps this from being implemented at the library level as templated type: NotNull!(T) ? If there are limitations, maybe these areas of the language can be improved to get us there? NotNull!T needs to have its default constructor disabled. That is the #1 blocker. There are other problems, mostly related to this - classes and structs with NotNull!T fields must define a constructor, and for structs that means the default destructor either needs to be possible to define or possible to disable. Arrays of NotNull!T may not be resized to a greater length, as that would require calling the default constructor of NotNull!T. I think that's it. Not exactly disabled. The following should work: class Foo { this() { bar = new Object(); } NonNull!(Object) bar; // fine here } while the following shouldn't: NonNull!(Object) bar; // error here bar = new Object(); I belive you have misunderstood me. The NonNull struct would need to have its default constructor disabled. Then, extra logic would need to be added to class and struct constructors to ensure NonNull fields have their constructors called. Context-sensitive constructor disabling is a theoretical possibility, but seems to me to conflict with D's other goals. -- Simen
Re: null [re: spec#]
Jonathan M Davis wrote: ??? Structs have no default constructor. True. Disabling the default constructor here means that struct Foo{ @disable this(); } Foo f; should halt compilation at Foo f;, and certain other places where a default constructor would normally be used. They have an init property with is used for default initialization. Indeed. And this is different from a default constructor that you can't touch how? And if you could enforce that a struct be properly initialized rather than default initialized, that would be a big change. Indeed. And that is what we are trying to achieve. Either init needs to be changed to allow for real default constructors and/or objects need to be legal CTFE - ideally both. Disabling the default constructor (which ostensibly looks this: this( ) { this = init; }) is a very good step in the right direction. Having proper default constructors would be better, and I can not remember the reason we don't have that. So, there is not currently a way to have NotNull!T hold anything but null initially when it's a member variable of a struct, and there's no sign of that changing any time soon. Indeed, and this will never change - it can't. However, we are arguing that it could be null before the struct's constructor is called, and flow control (or out contracts, if necessary) enforce that it is not null when the constructor returns. I admit I here somewhat miss C++'s initialization lists: struct Foo { NonNull< Bar > bar; Foo( ) : bar( new Bar() ) { } } -- Simen
Re: null [re: spec#]
Jonathan M Davis wrote: I was not aware of that. I didn't think that you could do that for structs since default constructors are illegal in the first place. And you can't. That doesn't mean it can't be possible in the future. It would likely have the negative side effect of making it illegal to put Foo in arrays though, since it wouldn't be possible to fill in the array values with init. Indeed. That's been discussed here, and in short, these are the rules: Arrays of non-null cannot have their lengths increased. To create an array of non-null, use a literal, a function that checks all parameters and builds an array from it, or a function that checks all elements of a passed array of nullable elements. To create a longer array, append two arrays. > They have an init property with is used for default initialization. Indeed. And this is different from a default constructor that you can't touch how? Default initialization is used all over the place - including both stuff like the declaration Foo f; and in array declarations. Every type is default initialized when not initialized directly, and it has nothing to do with default constructors. init must be known at compile time whereas default constructors are run at runtime. I mean, the compiler is doing something to move .init to the new instance of the type, and this could very well be considered the default constructor. init gets used a fair bit (such as when you declare an array of a given size and the compiler _must_ initialize all of its elements), and it's vital to how D functions. static arrays would require initializers in this case: NonNull!(T)[2]; // Compile-time error: NonNull!T has no default constructor NonNull!(T)[2] = [nonNull(new T), nonNull(new T)]; // Works Depending on exactly how init works, it could be a _huge_ change to try and make a default constructor run instead. Possibly, though I don't think so. As said before, some code needs to be executed to copy .init to the new instance. This code could likely be replaced with a default constructor. Even so, the disabling of default constructors is not really about constructors, but as you say, about disabling .init. This will not require execution of new code, only a compile-time check for whether the constructor has been disabled. -- Simen
Re: null [re: spec#]
Jonathan M Davis wrote: We definitely want to avoid the nastier issues, but the lack of default constructors continually pops up as being a major problem. The QtD folks in particular were have a lot of problems because of it as I understand. I've certainly had problems with it my code. Even a restricted default constructor would be better than none. In my experience, disabling the default constructor would be enough. That way the compiler warns you that 'this object has no valid default state, so use a constructor.' If we could have default constructors on structs but have it so that any struct with a default constructor cannot be used where you have to have a bare init with no constructor call And where is that? Currently no such thing exists, as we have no default constructors for structs. That means we could interpret any use of .init as a constructor call. Disabling default constructors has this same advantage, while avoiding the problems. The only problem is you will have to use a constructor explicitly. -- Simen
Re: null [re: spec#]
Daniel Gibson wrote: Nick Sabalausky schrieb: "so" wrote in message news:op.vlv3iukp7dt...@so-pc... There's no usage of an undeclared variable, but the right-hand-side of the second line uses 'i' before *the programmer* initializes it. Yes, the D compiler chooses to automatically initialize it, but by doing so it silently creates a bug every time the programmer intends 'i' to start out as anything other than 0. And it's not easily noticed since 0 is a commonly-used value. (Something like 0xDEADBEEF would at least be an improvement (albeit a small one) since at least that would stand out more and likely fail more spectacularly.) So you want language force you to type either "int x=0;" or "int x=void;". Fair enough and i agree it "might" be a bit better. But you are making it as it is something so much important. I tend to get a bit fired up by it because Walter's reasoning on it being *better* to automatically assume some init value baffles me. It gives deterministic results/errors. Yup. Also, as opposed to certain other solutions, it does not require advanced flow control, that is likely to be incomplete. Incomplete flow control here will make people write code 'to shut the compiler up'. And that is worse than uninitialized variables. -- Simen
Re: null [re: spec#]
Nick Sabalausky wrote: Also, as I've said when this was discussed in the past, I wouldn't even want the flow analysis to be perfect because all that would accomplish is to encourage garbage like the above (which would just end up being highly fragile anway). But if it isn't, we must either define what it should do, or accept that some compilers will catch some of the uninitialized variable bugs, where others do not. We do not want that. -- Simen
Re: class instance construction
spir wrote: First, what is the actual point of "new"? Mostly, it's a remnant from C++. I find this keyword rather helpful in that it reminds me the element is referenced/heap-allocated/GC'ed. But is there any ambiguity on the language's side? We cannot construct a class instance in any other way, AFAIK, and "calling" a class can hardly have any other sense. Maybe classes' opCall could be set to the creation routine (whatever new calls); then we would get a single object-creation syntax. Indeed it could. I believe Andrei Alexandrescu has discussed in favor of such a change in the past. Still, an other case when "new" annoys me is method chaining, because it makes syntax heavier and less readable: c = (new C(p)).do(x); versus: c = C(p).do(x); Or, maybe, the parser could be clever enough to correctly decode: c = new C(p).do(x); One would think it possible to stop searching right upon first finding a function call parentheses (as opposed to a template parentheses). This is complicated by the allowance of the syntax 'new C;'. Second, could there be a default constructor for classes, like for structs? Namely, one that sets declared fields: class C {int i;} ... auto c = new C(1); There could. :p As for whether there should, I'm not sure. I would say such a constructor should disappear, should the class designer add other constructors. That, coupled with classes usually being more complex than that (mine are, at least), this seems like a feature that would have little actual air-time. This is one great advantage of static languages, that the compiler knows more thank to declarations. Isn't it sensible to use this knowledge when helpful? I simply find the following stupid (I mean it's job for a machine, not for a programmer): class C { int i; this (int i) {this.i = i;} } As we already enjoy this feature for structs... The syntax this( int this.i ) {} has been suggested for automatic initialization of fields. I like this suggestion, but it is unlikely to find its way into the language any time soon. -- Simen
Re: why a part of D community do not want go to D2 ?
spir wrote: But why not design from the start new Phobos libs with inspiration from known-to-be-good Tango ones? We got kinda spooked by the incident with SHOO and std.time. It likely was not as bad as it seemed, but it brought some bad blood. -- Simen
Re: One year of Go
Nick Sabalausky wrote: try { return true; } finally { return false; } try { throw new AssertionError(); } finally { return false; } I wonder how D handles those Error: return statements cannot be in finally, scope(exit) or scope(success) bodies -- Simen
Re: The D Scripting Language
Per Ångström wrote: /++ Simulates type-returning or-expression +/ template or(T) { T _(T a, lazy T b) {T tmp = a; return tmp ? tmp : b;} } You should probably use a function template[1] or at least an eponymous template here: // function template: auto or( T )( T a, lazy T b ) { return a ? a : b; } // eponymous template: template or( T ) { auto or( T a, lazy T b ) { return a ? a : b; } } [1]: http://digitalmars.com/d/2.0/template.html#function-templates -- Simen
Re: "In praise of Go" discussion on ycombinator
Matthias Pleh wrote: Am 17.11.2010 14:55, schrieb Steven Schveighoffer: Being someone who likes the "brace-on-its-own-line" style i++ Surely you mean: i ++ ; -- Simen
Re: casting class pointer
Jesse Phillips wrote: auto hi = "hello" in b.all; auto foo = cast(B) hi; The result was that b.label wasn't the same, and printing out stuff resulted in a bunch of garbage. Not surprising. hi is pointing to a reference (pointer) to a B instance, not a proper B, and certainly not the one you hope for. Thus, random garbage from the memory surrounding the reference is outputted. The questions I have are, should casting a class pointer to a class change its behavior so it just gives you the class? Is is there a use case for the current behavior? And should I bother working to reproduce this? The idea behind the in operator for AAs is that it indicate whether there was something at the specified index, by returning null if there wasn't. IOW, 'a in AA' does not return a class reference, and shouldn't. You may have reasons to put nulls in your AA, in which case the returned B* may be non-null, while *hi is null. I have not thought of a good use case for this, though. However, the main reason I see is symmetry. Having to explain that for T[U] t, 'a in t' returns a T* is ok. Having to explain that this simple rule breaks when T is a class, is not good. Last, a cast is a message to the compiler of "Trust me, I know what I'm doing". That could mean that your B* is actually a B (a reference), that somehow ended up being a B*. In such a case, the only reasonable thing to do is interpreting the bits as a B instead of a B*. What you should do in the above code is auto foo = *hi; tl;dr: making the casting of class reference pointers to class references do the shortcut of interpreting the pointer as a reference is asymmetric and limits the language. -- Simen
Re: Repairing BigInt const
Matthias Walter wrote: On 11/21/2010 04:33 PM, Don wrote: Matthias Walter wrote: as it seems, the current version of BigInt is not capable of const, i.e. BigInt(1) + const(BigInt)(1) does not work. Is there already an effort to fix this or would it make sense if I had taken some time to create a fix for it? I have no idea of all the asm in the x86 specialization but as const is an interface thing, I should be able to figure out everything without that knowledge. Or are there general design problems, such that this would be a waste of time? It's been prevented by some compiler bugs. The changes to pure in 2.050 were triggered by attempts to make BigInt pure. 2.051 will include several fixes to nothrow. You mean by "making BigInt pure" that all the computation-methods (like opBinary, etc.) will be pure, right? Or can D structs be pure as well? (Whatever this would mean...) "Making BigInt pure" means making it usable in pure functions. That is, make all member functions pure that can be pure. -- Simen
Re: Basic coding style
bearophile wrote: From what I'm seeing in D.learn there is a problem in the D community: some people seem irrationally attached to the idea they are free to write D code as they like. I have four or five times tried to explain why coding standards are important for the development of the D community, but it seems not everybody is understanding it. First off, in your posts on D.learn, you come off as overzealous and vindicatory, as if you are on a holy crusade for The One True Style™. This, I believe, is not a good way to greet newcomers. That said, I agree there should be a One True Style™, and here are my suggestions: Make the style in three parts, where one is considered nigh obligatory, and concerns public interface only. That is, names and structure. The second part concerns only official D code. That is, if you want your code in Phobos, make sure it follows these rules. The third part should be guidelines to beautiful code. This is a somewhat subjective measurement, and should be threated as such. Whenever code is posted on the newsgroup that does not follow part 1, feel free to suggest that the poster read the style guide. If it does not follow part 2, leave it alone. If it blatantly violates the intent of part 3, link to part 3. -- Simen
Re: name guess by the compiler
On Thu, 25 Nov 2010 11:10:29 +0100, spir wrote: PS: for got to ask: is there any kind of namespace in D -- especially one kind usable for module export? D does not have namespaces per se. However, modules introduce a sort of namespace, templates create parametrized namespaces, and final abstract classes may be used to fake namespaces, should the other features not be sufficient: final abstract class MyNamespace { static: // stuffs } I have no idea how dmd guesses possible names. But surely there is a kind of probability evaluation used to rank possible guesses. If I'm right, then dmd should not output its best guess in every case, but only when its probability value is higher than a given threshold (to be carefully defines). This sounds reasonable, but hardly top of the list on urgency. Also, the case above probably reveals some kind of bug, since it is hard to imagine how "to" can ever be the best guess for "i" (and there is no alias in the test case). PS: "alias to" is only guessed when the module imports std.conv:to. Still, how to guess "to" from "i"? Finally, should the guessing feature exclude (unqualfied) imported symbols? Absolutely not. -- Simen
Re: What are modules for?
rickned wrote: Modules, files, packages, compilation units... I have a class that is designed to be derived from. Can I put it in a module (file) so other people can import the module (file) like a C-c++ header so they can derive from it? Yes. Any non-final public class in an imported module can be derived from. Is that the idomatic way to do that in D? There is the option of using a .di file, if you want to hide implementation. .di files are normal D source files, with implementation stripped. Due to the compilation speed (I believe) and no requirement to hide implementation, most projects I've seen simply use .d files. I don't want to expose the method definitions, mind you. I understand deeply the C++ way of doing things like hiding implementation and interfaces etc. Can someone bring it all together for me in D? See above. .di files are basically what you'd expect to be the D equivalent of a .h file. And note that the hiding part is not the gist of my question, but rather that I want to understand the D way but am probably trying to contrast it to the C++ way. I have TDPL (borrowed from the library, and it's a one without Andy's name on the cover), but I started reading it in the module chapter but found it rather confusing, if not lacking all the pertinent info. -- Simen
Re: What are modules for?
Jonathan M Davis wrote: You can, create .di files which have function declarations leaving the definitions in a .d file ( http://www.digitalmars.com/d/2.0/dmd- linux.html#interface_files ), but those are specific to dmd (as opposed to the D language) This is false. .di files are simply .d files with a different extension. Any compiler that supports the compilation of .d files will support .di files (granted, it may only look for .d files). And I'm not sure that those work very well with templates, since you need the whole template definition when instantiating a template, and templates are typically very heavily used in D. Template definitions are left in the .di file. -- Simen
Re: What are modules for?
Jonathan M Davis wrote: On Friday 26 November 2010 00:44:03 Simen kjaeraas wrote: Jonathan M Davis wrote: > You can, create .di files which have function declarations > leaving the definitions in a .d file ( > http://www.digitalmars.com/d/2.0/dmd- > linux.html#interface_files ), but those are specific to dmd (as opposed > to the D > language) This is false. .di files are simply .d files with a different extension. Any compiler that supports the compilation of .d files will support .di files (granted, it may only look for .d files). I believe that Walter has stated that .di files are a dmd-specific thing and are not actually a part of the D language. That doesn't stop other compilers from supporting them, but they are effectively a dmd-specific language extension. The language itself specifies .d files as being d source files but says nothing about .di files. They are not a defined part of the D language because they don't need to. They are simply D source files. The dmd-specific part of it is that dmd supports generating .di files from .d files. -- Simen
Re: Logical const
Jonathan M Davis wrote: So, _I_ am certainly not aware of a good solution, but if you play games, you should be able to get _something_ working. Overall though, I think that the lesson is that you should really be making const functions truly const and not try and deal with logical const at all. Highly unsafe, but works in the cases you'd expect. Use with extreme caution: import std.traits; @system ref Unqual!T unqualified( T )( ref const T t ) { Unqual!(T)* p = cast(Unqual!(T)*)&t; return *p; } -- Simen
Re: const a storage class or a type modifier?
Andrej Mitrovic wrote: D's slogan should be "There's a keyword for that." :p And here I thought we only needed 'static'... -- Simen
Re: Logical const
Walter Bright wrote: immutable - Data will never, ever change, through any reference. const - Data will never change through this reference. newlevel - Data will never logically change through this reference. The problem with newlevel is (to repeat myself) it is unverifiable. As long as it is transitive, I believe it is verifiable: struct S { int n; } struct foo { int n; foo* p; newlevel S s; newlevel int m; void bar( ) newlevel { m++; // works, m is newlevel n++; // compile-time error, n is const for newlevel this s.n = 3; // Works, s.n is newlevel p.n = 4; // compile-time error, p is newlevel, p.n is const for // newlevel this p.m++; // Works, p.m is newlevel } void baz( ) const { m++; // compile-time error, m is const for const this } } void qux( newlevel foo f ) {} void quux( ref foo f ) { f.n++; } void main() { const foo f; f.bar( ); // compile-time error, f is const, cannot call newlevel // functions on it qux( f ); // compile-time error, const(foo) cannot be implicitly // cast to newlevel(foo) newlevel foo g; quux( g ); // compile-time error, newlevel(foo) cannot be implicitly // cast to foo } This leaves const as a strong, transitive guarantee, and newlevel as a transitive guarantee that only newlevel members may be changed. I am not convinced that such an extension of the type system should be made, but I believe you are wrong in that it is not verifiable. but please, do show me my mistake. -- Simen
Re: Logical const
Steven Schveighoffer wrote: Except the language says what results from your code (casting away const and then mutating) is undefined behavior. This means all bets are off, it can crash your program. I'm unsure how the compiler could take that route, but that's what's in the spec. Maybe because of the way const works, it never really is undefined, but there will always be that loophole. The thing is, immutable is implicitly castable to const, and immutable data could be stored in write-protected memory. Apart from that, I believe it is safe to cast away const. -- Simen
Re: D's greatest mistakes
Andrej Mitrovic wrote: Why not do the same for templates? Something like this: void foo(A, B, C)(A a, B b, C c) constraint { isDynamicArray(a); isIterable(a); } Consider: void foo( A )( A a ) constraint { isBar!A; } void foo( B )( B a ) constraint { isQux!B; } Which error message should I show? -- Simen
Re: Logical const
Walter Bright wrote: Simen kjaeraas wrote: I am not convinced that such an extension of the type system should be made, but I believe you are wrong in that it is not verifiable. but please, do show me my mistake. It is not verifiable because nothing prevents you from assigning: m = random(); That is not logical const. Pardon my stubbornness (and perhaps ignorance), but how is this any less logical const than is 'm = 4;' or 'm = n * 3 + cast(int)phaseOfMoon();'? -- Simen
Re: D's greatest mistakes
Andrej Mitrovic wrote: Which error message should I show? I'm not sure what you're getting at. :) [snip] But anyway it's just an idea and I'm probably missing something important, I haven't used D in a while.. I believe you are missing that more templates could bear the same name, and they could all fail for varying reasons. In such a case, do you print the error messages from 100 templates and let the user sort through them, or do you show the error the compiler currently shows? I admit my example was flawed, or at least incomplete: void foo( A )( A a ) constraint { isBar!A; } void foo( B )( B a ) constraint { isQux!B; } void main( ) { int n; assert( !isBar!int ); assert( !isQux!int ); foo( n ); } The call to foo would match neither template, giving errors akin to this: test.d(15) Error: template instantiation foo!int failed. test.d(3) Error: template constraint failed test.d(6) Error: template constraint failed Seems good for two or three templates, but I fear this would grow quickly out of hand for more templates. -- Simen
Re: Logical const
Jesse Phillips wrote: In fact, couldn't opAssign be ref opAssign(U)(auto ref U v) if(Unqual!U == U) { return mutable() = v; } This way only mutable references are only ever assigned to it? Then it can be marked as @trusted and used in safe code as it will never cause the program to crash? Indeed it could. This brings me to this code: struct Mutable( T ) if ( is( T : Unqual!T ) ) { private T _payload; this( Unqual!T t ) { _payload = t; } @trusted @property ref T get( )( ) const { T* p = cast( T* )&_payload; return *p; } alias get this; @trusted ref opAssign( U )( auto ref U u ) const if ( is( Unqual!U == U ) && is( U : T ) ) { T* p = cast( T* )&_payload; return *p = u; } bool opEquals( )( ref const Mutable other ) const { return _payload == other._payload; } bool opEquals( U )( auto ref U other ) const if ( is( U : T ) || is( T : U ) ) { return _payload == other; } } It works in many cases, but not for function calls: void bar( int n ) {} class A { Mutable!int n; } A a = new A; bar(a.n);//Error: function bar (int n) is not callable using argument types (const(Mutable!(int))) -- Simen
Re: Logical const
Simen kjaeraas wrote: This way only mutable references are only ever assigned to it? Then it can be marked as @trusted and used in safe code as it will never cause the program to crash? Indeed it could. Wait. Correction: In the case when the object harboring the mutable member is moved into read-only memory (I do not know if this might actually happen), this ceases to be true. -- Simen
Re: Logical const
Simen kjaeraas wrote: It works in many cases, but not for function calls Some more testing brought this bug to my attention: void bar( ref int n ) { n++; } void main( string[] args ) { const int n = args.length * 0; assert( is( typeof( n ) == const(int) ) ); bar( n ); // Uhm... assert( n == 1 ); // WTF?!? } I would say this is seriously bad. http://d.puremagic.com/issues/show_bug.cgi?id=5291 -- Simen
Re: Logical const
Walter Bright wrote: Logical const means the same value is returned every time, not a different one. So you would have only pure functions work with logical const? The new keyword 'newlevel' defined earlier could certainly include pure. Also, the compiler could have the automatically generated opEquals ignore fields marked 'newlevel'. This way, logical constness is preserved, no? Going further, one could define mutable state only in (pure) member functions. This state would actually be stored in the object, but would be inaccessible to other member functions, and would not be considered by opEquals. I believe this is the closest we could come to enforcing logical constness. -- Simen
Re: Logical const
Andrej Mitrovic wrote: Oh this one is even more spectacular: void bar( ref int n ) { n++; } void main( string[] args ) { immutable int n = args.length * 0; assert( is( typeof( n ) == const(int) ) ); bar( n ); // Uhm... assert( n == 1 ); } K:\code>dmd bug.d -release -O -inline K:\code>bug object.Error: assert(0) or HLT instruction I'll update the bugzilla entry. Don't. It's your first assert, failing as const(int) != immutable(int) -- Simen
Re: Logical const
Walter Bright wrote: It still is not verifiable. That's why logical constness is not a language issue, it is a convention. And finally I understood what you meant. Sorry about this taking a while. -- Simen
Re: D's greatest mistakes
bearophile wrote: I like very much the idea of constraint blocks. Much better than expanding func header line, reuses an existing syntactic construct, and far cleaner. But I'd like that syntax to allow for an error message for each constraint. So it becomes like contract programming, done at compile-time on types and compile-time constants, and the "in" keyword is enough: in { static assert(isDynamicArray!T, "err msg 1"); static assert(isIterable!T, "err msg 2"); ... } body { ... } The problem with such a solution is that the compiler needs to evaluate the 'in' clause differently from all other code, as code further down in the 'in' clause could use parameters in a way that would be illegal at compile-time. Hence the new keyword or, for giggles, why not 'static in'? -- Simen
Re: D's greatest mistakes
bearophile wrote: I think the syntax of is() is awful and I'd like to shoot it. In my opinion, the syntax for is() is not all that bad. However: - It lacks is ( Type : TypeSpecialization , TemplateParameterList ) and is ( Type == TypeSpecialization , TemplateParameterList ) - It does not deduce base templates. Given is ( T == U!V, U, V... ), it fails. Given is ( T == MyTemplate!U, U... ), it works. -- Simen
Re: D's greatest mistakes
Steven Schveighoffer wrote: On Tue, 30 Nov 2010 13:13:47 -0500, Steven Schveighoffer wrote: IMO opinion, IMO opinion? Hm... wonder what that O stands for :D Own? -- Simen
Re: D's greatest mistakes
bearophile wrote: - It lacks is ( Type : TypeSpecialization , TemplateParameterList ) and is ( Type == TypeSpecialization , TemplateParameterList ) I think is() needs less stuff, not more. The functionality needs to be moved to a nicer syntax (and according to Andrei a better semantics too). The versions I suggest 'is' lacks are simplifications of existing options, and would IMO simplify the feature. I also feel that is() is logical. That is, the parts without the Identifier. is( A : B ) makes sense, as does is( A == B ) and is( A : B!T, T... ). is( A B : C!T*, T... ), on the other hand... -- Simen
Re: delegates and heap usage
Franciszek Czekala wrote: What part of stack frame is exactly copied when a delegate is used? Generally, only the used variables. You can check the size of the closure's stack copy using GC.sizeOf(dg.ptr). For example what is the heap usage due to delegates usage in the following code? Adding this code to the end of it: int n = 0; void* ptr = null; foreach ( e; dlg ) { if ( e.ptr != ptr ) { ptr = e.ptr; n += GC.sizeOf( e.ptr ); } } writeln( n ); We can see that 1600 bytes are used. The library reference speaks about the enclosing function but I do not think that it suffices to copy the stack frame of the directly enclosing function. Is the whole stack copied? All enclosing functions need to have their stack frames copied. That is, not the whole stack, but any functions for which the delegate would be a nested function. What happens in the case of recursive calls? int recurse( int delegate() dg ) { int n = 1; return GC.sizeOf( dg.ptr ) + ( dg() ? recurse( { return dg() - n; } ) : 0 ); } void main(){ writeln( recurse( { return 100; } ) ); } Outputs 1600. -- Simen
Re: tail const
vincent picaud wrote: To summarize there would be just one keyword to add in D : "const_". This attribute would have sense only for Objects (the same logic would also hold for "immutable_"). To my IMHO the syntax const_ is easy to unerstand, because "_" clearly shows the place holder position and its missing "const" (remembering C++) The issue is not with syntax (I am of the impression that most who want this, like Michel Fortin's const(A) ref a). The problem is Walter does not want to do it, as he considers it impossible to get right. Of that I'm not sure, but he's proven me wrong in the past, so it could happen again. -- Simen
Re: delegates and heap usage
Franciszek Czekala wrote: I am not sure that this is a correct answer. 1600 bytes is just 100x2x8 bytes. Since delegates are fat (double) pointers this is probably just the memory occupied by dlg variables themselves, not the memory to which they point. Indeed adding an extra z variable in my code to the g() function and using it in writeln does not change the output: it is still 1600 bytes. Wrong. The delegates would be 100x2x4 bytes, as DMD only produces 32-bit exes. The 16 bytes per delegate is because that's the smallest block the GC will allocate. Proof: long recurse( long delegate() dg ) { long n = 1; long m = 0; long r = 0; long s = 0; long t = 0; return GC.sizeOf( dg.ptr ) + ( dg() ? recurse( { return dg() - n + m - r + s + t; } ) : 0 ); } void main(){ writeln( recurse( { return 100L; } ) ); } Outputs 6400. -- Simen
Re: tail const
Michel Fortin wrote: I'm not sure I get the problem. Can you show me in code? const a = map!"a+a"( [1,2,3] ); foreach ( e; a ) { } The foreach fails because popFront is not const. What is needed is for typeof(a) to be Map!("a+a", const(int)[]). IOW, is( const(Map!("a+a", int[])) == Map!("a+a", const(int)[]) ). One possible way to do this is for all types T to have defined types immutable_t and const_t, which by default alias to immutable(T) and const(T), but can be defined to alias to other types. The compiler would then automagically convert cast(const)T to cast(T.const_t)T. -- Simen
Re: tail const
Andrei Alexandrescu wrote: On 12/2/10 6:54 PM, Simen kjaeraas wrote: Michel Fortin wrote: I'm not sure I get the problem. Can you show me in code? const a = map!"a+a"( [1,2,3] ); foreach ( e; a ) { } The foreach fails because popFront is not const. What is needed is for typeof(a) to be Map!("a+a", const(int)[]). IOW, is( const(Map!("a+a", int[])) == Map!("a+a", const(int)[]) ). One possible way to do this is for all types T to have defined types immutable_t and const_t, which by default alias to immutable(T) and const(T), but can be defined to alias to other types. The compiler would then automagically convert cast(const)T to cast(T.const_t)T. Well the code asks for a constant object, and I don't see it as reasonable for the type system to automagically infer the intent. True. I believe I was thinking that T should be implicitly convertible to T.const_t (or tailconst_t, as may be more appropriate). What might be appropriate is a function tailconst( T )( T t ) that returns a tail const version of the passed type. That is, given a T[], const(T[]), const(T)[], immutable(T[]), or immutable(T)[], it returns a const(T)[]. For a MyRange!R, const(MyRange!R), or immutable(MyRange!R), it returns a MyRange!(R).tailconst_t. See bottom of post for a (naïve) implementation. What should work is this: const(int)[] data = [1,2,3]; auto a = map!"a+a"(data); foreach (e;a) { } That does work. import std.traits; /** * Return the tail-const type for a given type **/ template TailConst( T ) { static if ( is( T U : U[] ) ) { alias const(Unqual!U)[] TailConst; } else static if ( is( T U : U* ) ) { alias const(Unqual!U)* TailConst; } else static if ( is( T.tailconst_t ) ) { alias T.tailconst_t TailConst; } else static assert( false ); } unittest { struct test { alias int tailconst_t; } assert( is( TailConst!( int[] ) == const(int)[] ) ); assert( is( TailConst!( immutable( int[] ) ) == const(int)[] ) ); assert( is( TailConst!( const(int)[] ) == const(int)[] ) ); assert( is( TailConst!test == int ) ); } /** * Converts the given parameter to tail const **/ TailConst!T tailconst( T )( T t ) { TailConst!T tmp = t; return tmp; } unittest { struct test( T ) { alias test!(const T) tailconst_t; this( const test!( Unqual!T ) t ) {} } assert( __traits( compiles, { tailconst( [1,2,3] ); } ) ); assert( __traits( compiles, { test!int t; tailconst( t ); } ) ); } -- Simen
Re: tail const
Simen kjaeraas wrote: What might be appropriate is a function tailconst( T )( T t ) that returns a tail const version of the passed type. That is, given a T[], const(T[]), const(T)[], immutable(T[]), or immutable(T)[], it returns a const(T)[]. For a MyRange!R, const(MyRange!R), or immutable(MyRange!R), it returns a MyRange!(R).tailconst_t. See bottom of post for a (naïve) implementation. To expound further on this, I have created the attached module. Critique wanted. -- Simen tailconst.d Description: Binary data
Re: tail const
Andrei Alexandrescu wrote: On 12/4/10 12:23 PM, Simen kjaeraas wrote: To expound further on this, I have created the attached module. Critique wanted. Looks promising. A few comments. * For TailXxx you need to handle built-in simple types (int, float...) to return themselves. Also, structs for which hasIndirections returns false also return themselves. Done. * tailconst_t does not obey Phobos' naming convention. I think it's fine to use TailConst in spite of the apparent ambiguity. It adds some .'s: alias SimpleRange!(.TailConst!T) TailConst; static if ( !is( T == .TailMutable!T ) ) { this( SimpleRange.TailImmutable r ) { Not sure if this is a problem. * You may want to add more stringent checks for tailconst_t (well TailConst etc) to make sure it's not bogus - has the same size, compatible members etc. I've tried, and it seems adding static assert( T.sizeof == T.TailConst.sizeof ); does not work (no size yet for forward reference). Checking that all members are the same type and order is easy and works. Comparing member names bumped me into bug 5079. Likely related to the above, seeing as both have to do with unfinished types. I've also considered a template on the form mixin tailConst!( SimpleRange, SimpleRange!( Tail!T ) ); or mixin tailConst!( SimpleRange, Tail!T ); which would automagically define aliases. More work for me, perhaps less work for users. This could not define constructors, as overloads from inside template mixins don't work with overloads outside. -- Simen tailconst.d Description: Binary data tailconst2.d Description: Binary data
Re: tail const
Simen kjaeraas wrote: I've also considered a template on the form mixin tailConst!( SimpleRange, SimpleRange!( Tail!T ) ); or mixin tailConst!( SimpleRange, Tail!T ); A closer look at this reveals that it won't work that simply, because SimpleRange in this context is the struct, not the template. This, however, works: mixin tailConst!( .SimpleRange, TailT! ); Not sure how I like this. -- Simen
Re: const(Object)ref is here!
spir wrote: On Mon, 6 Dec 2010 00:31:41 -0800 Jonathan M Davis wrote: toString() (or writeFrom() or whatever it's going to become) guess it was writeTo() ;-) but "writeFrom" is nice as well, we should find some useful use for it This reminded me of INTERCAL's WRITE IN and READ OUT. -- Simen
Re: const(Object)ref is here!
Michel Fortin wrote: And that's what my patch does. It only gets confusing when you add modifiers: alias const(Object)ref A; alias const(Object ref) B; alias const(Object) C; A ref a; B ref b; C ref c; With my patch, variables 'a', 'b', and 'c' are all of the same type: "const(Object)ref", the later 'ref' changing the constness of the reference specified in the 'B' alias. const(Object ref) is implicitly castable to const(Object) ref, no? So to me, it seems all of these should be const(Object) ref. Seeing Foo ref name, I read this as 'non-const reference to some class'. IOW, if you want a rebindable reference to a class T, the type of T should not affect what hoops you must jump through to get there. -- Simen
Re: Scripting again.
so wrote: Hello! In D, it is much harder to say something is impossible to implement comparing to other languages. If you are coming from C/C++ land, you should know that if you want to script your program, you are going to fall back to another language, possibly a "scripting" language. But why? why should i fall back to a dummy language for this? Tons of workarounds to export stuff, it is just a mess! It doesn't matter if we have tools like boost python bindings or PyD, it is still ugly. All i can see right now is the lack of compile(file, module dependencies, ...). Is it just technical limitations or is this is another religious issue? Mostly, there is no D compiler written in D that can be included in the standard library. -- Simen
Re: rationale: [] and ()
Manfred_Nowak wrote: What is the rationale for having both: normal and square brackets? Mostly that C has both. Changing this would make the language feel rather more different from C/C++ than it already does. It is also syntactic sugar for element lookup. Rather than having to write some function name (that may or may not be the same for all collections), you can use []. I ask because in plain old C I interpreted the lexical difference of funtion calls and accesses to elements of arrays as a permanent hint for linear runtime in case of arrays. But because of `opIndex' this assumption has been invalidated a long time ago. No it hasn't. opIndex should still be O(1), it just can't be enforced. -- Simen
Re: rationale: [] and ()
Steven Schveighoffer wrote: On Fri, 10 Dec 2010 09:42:51 -0500, Simen kjaeraas wrote: Manfred_Nowak wrote: But because of `opIndex' this assumption has been invalidated a long time ago. No it hasn't. opIndex should still be O(1), it just can't be enforced. er.. make that O(lg(n)) :) Essentially sub-linear. Ah, yes. Sorry. -- Simen
Re: Verbose checking of range category
Jonathan M Davis wrote: It would be great if dmd said which constraint failed, but since you're dealing with an arbitary boolean expression, in many cases, would not be particularly straightforward to say what failed, and I expect that it would be a _big_ change for the compiler. Not to mention SFINAE, which would lead to a wall of text when multiple specializations of a template exists (15 templates failed, each for 3 different reasons = 60 lines of error messages, none of which are likely to be useful). And in such a case, no error messages are to be printed if one match is found, right? -- Simen
Re: const / in
Jonathan M Davis wrote: It has definitely been suggested before that const should be the default, and some people would love that, but there's no way that it's going to happen in D2, and much as I like const, I think that it would be a huge mistake to do it in any version of D and likely any version of any C-based language. It's too big of a break with other versions of D and other C-based languages. I'm torn about the issue. I see reasons to have const by default (or even immutable), but having it as the 'real' default would as you say, turn away too many programmers. Hence, I suggest that modules may be marked default-const: const module foo; int n; // const int mutable int m; // int This could also be extended to cover pure: pure const module foo; Is this a good idea? No idea. -- Simen
Re: Why Ruby?
so wrote: I am not here asking for syntax or commanding phobos team to implement something, I just want to know about technical limitations and license issues, but got no answer to these. Why? Either i am labeled here as a troll (maybe something worse since my usage of English is not the best) Your posts have at times come off as less than polite, but your knowledge and insight have shown it to likely be caused merely by limited mastery of the language. -- Simen
Re: Why Ruby?
Ary Borenszweig wrote: Code is read many more times than it is written and so it is of huge important that code is as readable as possible. Of course this is a subjective matter, but I don't understand why some people think __traits or __gshared are ok. __gshared is ok. It is supposed to indicate low-level hackery and unpleasant, rough edges. __traits, however, is an abomination and should be shot repeatedly with something high-caliber. -- Simen
Re: String to boolean inconsistency
Andrej Mitrovic wrote: Actually I'm having a hard time understanding this: void main() { string s = ""; assert(s); // pass, but why? assert(s !is null); // pass } void main() { string s = "".idup; assert(s); // fail assert(s !is null); // pass } Try adding writeln( s.ptr ); in there. Should probably give you an indication of what is and == do. Answer: == null: Length P==0 !=0 t ==0 T F r !=0 T F is null: Length P==0 !=0 t ==0 T F r !=0 F F Where T and F stand for true and false, respectively. So likely, idup on an empty string returns an array with null ptr and 0 length, while "" is 'allocated' in the data segment, and thus given a ptr value. -- Simen
Re: String to boolean inconsistency
Ellery Newcomer wrote: I forget, why are we supposed to use is instead of == with null? '[] is null' compares ptr and length, while '[] == null' compares only the length. Weirdly though, '[] is null' is false for ptr == 0, length != 0. Not likely to happen much in practice. -- Simen
Re: String to boolean inconsistency
Simen kjaeraas wrote: Ellery Newcomer wrote: I forget, why are we supposed to use is instead of == with null? '[] is null' compares ptr and length, while '[] == null' compares only the length. Weirdly though, '[] is null' is false for ptr == 0, length != 0. Not likely to happen much in practice. To actually answer your question, == calls opEquals for classes, and that is called on both lhs and rhs. null.opEquals however, segfaults. is, on the other hand, compares pointer equality, without caring for the details of what opEquals wants to do. -- Simen
Re: String to boolean inconsistency
Jonathan M Davis wrote: This behavior is intended. Arrays are actually something like this under the hood: struct array(T) { T* arr; size_t length; } Actually, that is: struct array(T) { size_t length; T* ptr; } To get the layout and names right. ( http://digitalmars.com/d/2.0/abi.html ) -- Simen
Re: String to boolean inconsistency
spir wrote: On Sun, 12 Dec 2010 04:00:36 +0100 "Simen kjaeraas" wrote: So likely, idup on an empty string returns an array with null ptr and 0 length, while "" is 'allocated' in the data segment, and thus given a ptr value. .dup & .idup should not change a string's truth value. For sure, _this_ is a bug. I think explicite empty strings/arrays should not have null pointers, as opposed to uninitialised strings/arrays. I'm not sure I agree here. It seems foolish to me to allocate 16 bytes (the minimum allocation size for the GC) for an empty array. Of course, "" is not really an empty array. Rather, it is the equivalent of "\n"[0..$-1]. That said, I think cast(bool)array should be true only in the cases where both ptr and length != 0; -- Simen
Re: Why Ruby?
Lutger Blijdestijn wrote: Simen kjaeraas wrote: Ary Borenszweig wrote: Code is read many more times than it is written and so it is of huge important that code is as readable as possible. Of course this is a subjective matter, but I don't understand why some people think __traits or __gshared are ok. __gshared is ok. It is supposed to indicate low-level hackery and unpleasant, rough edges. __traits, however, is an abomination and should be shot repeatedly with something high-caliber. Is std.traits not sufficient for that? I have yet to see std.traits.compiles( some code ). There is a very nice enhancement request in Bugzilla that __traits be replaces with a magic namespace. meta.compiles( some code ) is heads and heels superior to __traits( compiles, some code ). I've not checked if others of __traits' capabilities are impossible to implement as templates. It has also been suggested that __LINE__ and __FILE__ be stored in this namespace, but I find that rather less likely to happen. -- Simen
Re: Why Ruby?
so wrote: If we take a look at the very first code example from the talk it looks like this: account.people.each do |person| puts person.name end You could translate this in two ways when translating into D. First way: foreach (person ; account.people) writeln(person.name); Am i alone thinking D one better here? Absolutely not. Ruby reads like Yoda-speak, while D is almost plain English. Had foreach used 'in' instead of the semicolon, only punctuation and 'ln' would be off. -- Simen
Re: Why Ruby?
Nick Sabalausky wrote: void myfun(void delegate(string) lol) { lol("say it "); } void main() { myfun = (string what) { assert(0, what ~ " lol"); }; } I'm sure that's going to disappear when D's properties get implemented as intended. I'm not. Consider: @property void myfun(void delegate(string) lol) { lol("say it "); } void main() { myfun = (string what) { assert(0, what ~ " lol"); }; } -- Simen
Re: Why Ruby?
Andrei Alexandrescu wrote: fun (a, b ; c) stmt => fun(c, (a, b) { stmt }) This could and should be generalized for more parameters, which I'm sure is very useful: fun (a, b ; c, d) stmt => fun(c, d, (a, b) { stmt }) Of course "fun" could be actually "obj.method". With this we have a compelling syntax that has semantics obtained via lowering. This is very nice. UP VOTES!!1 -- Simen