Re: integral to floating point conversion
On Saturday, 2 July 2016 at 20:49:27 UTC, Andrei Alexandrescu wrote: On 7/2/16 4:30 PM, Walter Bright wrote: On 7/2/2016 1:17 PM, Andrei Alexandrescu wrote: So what's the fastest way to figure that an integral is convertible to a floating point value precisely (i.e. no other integral converts to the same floating point value)? Thanks! -- Andrei Test that its absolute value is <= the largest unsigned value represented by the float's mantissa bits. What is the largest unsigned value represented by the float's mantissa bits? I recall there is a constant somewhere for it. -- Andrei 2uL^^float.mant_dig And the same for double. For real (on x86), mant_dig is 64, so it can represent any ulong precisely. -- Simen
Re: Phobos: __FILE__ as template default parameter
On Monday, 20 June 2016 at 14:28:06 UTC, Jacob Carlborg wrote: Would it be a bad idea to allow this in the compiler: void foo(Args...)(Args args, string file = __FILE__, size_t line = __LINE__); It wouldn't be possible to pass "file" or "line" when calling "foo". But it's useful for the special default values, __FILE__ and __LINE__. I very much agree with this idea. Like with the current system of forwarding to a function with explicit file and line run-time arguments, a function can be made accessible that has them as explicit parameters for those cases when you want to pretend to be somewhere you're not. :p -- Simen
Re: C#7 features
On Monday, 9 May 2016 at 00:44:09 UTC, Peter Häggman wrote: Their tuples seem to be a complete DIY: https://msdn.microsoft.com/en-us/library/system.tuple(v=vs.110).aspx I wouldn't be surpised to see in the implementation an array of variant or something like that, explaining why it's limited to octuples [1]. Sharp tuples look weak compared to D tuple-ish things: Tuple, TList, AliasSeq, variadics, ... [1] Also I think that the param-"variadicity" is simply emulated via a set of overloaded constructor, explaining why they stop at 8. C#'s tuples are actually 8 different templated classes - one for each arity. There's a lot of duplicated code to make that work. Wait, it's actually 9 classes - in addition to Tuple through Tuplethere's the humble Tuple - a non-generic class that cannot be instantiated and only exists to be a namespace for the Tuple.Create function. The example code on gooroo seems to have eaten the template arguments for the constructor example - to instantiate a tuple you use one of these syntaxen: var t1 = new Tuple (1, "foo"); var t2 = Tuple.Create(2, "bar"); The 'templates' in C# are (much) more limited than old C++ templates, and have nothing on D's templates. That's not necessarily a bad thing, though - the language is different and fills a different niche. It does mean some things that are very elegant in D end up very inelegant in C#, though.
Re: Accessing __FILE__ and __LINE__ of caller in combination with varargs?
On Saturday, 16 April 2016 at 00:03:59 UTC, Jonathan M Davis wrote: On Friday, April 15, 2016 20:52:42 WebFreak001 via Digitalmars-d-learn wrote: void assertf(string file = __FILE__, size_t line = __LINE__, Args...)(lazy bool condition, in string message, Args args) { Yes, you can do that, but do _not_ do that unless you really have no other choice. What you need to realize is that because the file and line number arguments will be unique for every call to this function, it will generate a new instantiation of it _every_ time it is called. So, you're going to get a lot of code bloat. There are rare cases where such bloat would be acceptable, but in the general case, it's a terrible thing to do. This particular case might be acceptable given how short it is, but in general, using __FILE__ or __LINE__ as template arguments should be avoided like the plague. A few tricks to reduce this bloat: - Write a small wrapper. This will still give bloat, but only of small functions: void assertf(string file = __FILE__, size_t line = __LINE__, Args...)(lazy bool condition, in string message, Args args) { assertfImpl(file, line, condition, message, args); } - Only care about line numbers in debug mode. Makes debug more bloated, code less readable, and you lose out on line numbers in release. Still worth it occasionally: version (debug) { void foo(string file = __FILE__, size_t line = __LINE__, Args...)(Args args) { // Stuffs. } } else { void assertf(Args...)(Args args) { // Stuffs. } } I'd love to have a way to pass the file and line number info as regular parameters, though. Something like: void foo(Args...)(Args args, string file = __FILE__, int line = __LINE__) {} Sadly, currently not possible. Maybe we could overload @disable for this purpose? void foo(Args...)(Args args, @disable string file = __FILE__, @disable int line = __LINE__) {} -- Simen
Re: Opportunity: Software Execution Time Determinism
On Wednesday, 13 April 2016 at 22:58:26 UTC, Walter Bright wrote: The compiler could be fairly easily compute cyclomatic complexity, but how that would be used to determine max time escapes me. For example, how many times would a particular loop be executed? Isn't this the halting problem, i.e. not computable? The first step is simple - we care only about functions being constant-time. Let's invent a @keyword for that: @constanttime. @constanttime functions can only call other functions marked @constanttime, and may not contain conditionals, gotos or while-loops. @constanttime functions may contain for and foreach-loops, iff the number of iterations are known at compile-time, and 'break' is never used. The part about conditionals seems a bit harsh, but it's got to be there for determinism. Constant time is easy, and may or may not be enough to cover Nordlöw's needs. Anything beyond it is very likely to be halting problem stuff. Andrei has done some great work on determining big O complexity, but that's only a small part of this problem. I have a friend who works on timing attacks in cryptography. Knowing the implementation and measuring the time it takes to multiply two bigints can help you guess what the numbers are, so in his case @constanttime would be exactly what he wants, while big-O would be useless. Not knowing Nordlöw's use case I can't say for sure what he actually needs. -- Simen
Re: Units of Measurement Library: units-d
On Saturday, 2 April 2016 at 01:19:45 UTC, Meta wrote: What is needed is Lisp's gensym construct. That's basically what I said, no? :p One problem of lisp's gensym (if we were to use it in D) is that it's simply a monotonically increasing number with a global prefix. It's perfect for the language it's in, and all but useless in D. For this very reason, I have made a stab at implementing __GENSYM__ in D. It follows basically the ideas I outlined above, and spits out a string on the form MANGLE_#, with # being a counter for the given mangled name: module bar; struct Ham(string gensym = __GENSYM__) { pragma(msg, gensym); } struct Eggs(string gensym = __GENSYM__) { pragma(msg, gensym); } // === module foo; import bar; pragma(msg, __GENSYM__); // 3foo_1 void main() { pragma(msg, __GENSYM__); // _Dmain_1 Ham!() a; // _Dmain_3bar_1 Ham!() b; // _Dmain_3bar_2 assert(!is(typeof(a) == typeof(b))); Eggs!() c; // _Dmain_3bar_3 S2!() d; // _Dmain_3foo_1 } struct Qux { pragma(msg, __GENSYM__); // 3foo3Qux_1 void baz() { pragma(msg, __GENSYM__); // _D3foo3Qux3bazMFZv_1 Ham!() a; // _D3foo3Qux3bazMFZv_3bar_1 } } struct S2(string gensym = __GENSYM__) { pragma(msg, gensym); } Should I file an enhancement for this? -- Simen
Re: Units of Measurement Library: units-d
On Friday, 1 April 2016 at 21:46:35 UTC, ag0aep6g wrote: On 01.04.2016 22:59, Simen Kjaeraas wrote: The usual way to fix it would be to include __FILE__ and __LINE__ in the template arguments: Right, no mixin this way. I wouldn't call this "truly nice", though. It depends on code formatting to work. Put everything on one line and it breaks. Significant whitespace is a pain when generating code. Though this is not nearly as bad as significant indentation, of course. __FILE__ also kind of breaks separate compilation. All object files have to be compiled from the same directory. Otherwise __FILE__ will be different. __LINE__ has a similar (maybe even more obscure) issue. Add or remove a newline before compiling dependent modules and things break. Usually, one recompiles all dependents when a dependency changes, but a significant newline, really? I kinda agree. And looking at https://dlang.org/spec/traits.html, I see there's __MODULE__, which would probably be a better choice than __FILE__. As for __LINE__, what we'd want is basically something like __CONTEXT__, which doesn't exist, but might be the .mangleof of the surrounding scope: struct S(string ctx = __CONTEXT__) { pragma(msg, ctx); } S!() a; // "3foo" void bar() { S!() b; // "_D3foo3barFZv" } struct S2 { S!() c; // "S3foo2S2" void baz() { S!() d; // "_D3foo2S23bazMFZv" } } That'd remove the problem of significant whitespace. In fact, it'd also eliminate the need for __MODULE__ in this case. Still though, that's not enough if we want this to work: void foo() { alias a = Foo!(); alias b = Foo!(); assert(!isSame!(a, b)); } We could also add __COLUMN__, which would be the horizontal index of the instantiation's beginning: foo(3, Bar!3.baz); // ^Here. Position 11. Next problem: void main() { pragma(msg, __LINE__); mixin("pragma(msg, __LINE__);\npragma(msg, __LINE__);"); pragma(msg, __LINE__); } That prints '4' twice - once for the actual line 4, the other for the second line of the mixin. However, __FILE__ is different, so I guess __CONTEXT__ could also be. -- Simen
Re: Units of Measurement Library: units-d
On Friday, 1 April 2016 at 19:03:03 UTC, ag0aep6g wrote: I dislike that the type depends only on the given name. This effectively means that the names are in a global namespace. [snip] I can't think of a truly nice way to accomplish this, though. As far as I see, it needs a mixin of some kind. The usual way to fix it would be to include __FILE__ and __LINE__ in the template arguments: // In units/package.d: struct BaseUnit(string name, string symbol = null, string file = __FILE__, size_t line = __LINE__) { // ... } // in foo.d: import experimental.units; struct A { // Actual type is BaseUnit!("Ampere", "A", "foo", 5): alias BaseUnit!("Ampere", "A") Ampere; enum ampere = Ampere.init; } struct B { // Actual type is BaseUnit!("Ampere", "A", "foo", 12): alias BaseUnit!("Ampere", "A") Ampere; enum ampere = Ampere.init; } void main() { assert(!is(B.Ampere == A.Ampere)); } -- Simen
Re: foreach_reverse and lockstep.
On Friday, 1 April 2016 at 17:44:40 UTC, ZombineDev wrote: On Friday, 1 April 2016 at 16:10:01 UTC, Simen Kjaeraas wrote: On Friday, 1 April 2016 at 14:37:36 UTC, Jonathan M Davis wrote: (you'd need some sort of opApplyReverse function to go with foreach_reverse, and no such thing exists). Actually, it exists and it works. It's just not implemented for lockstep. I'm trying to clean up my Phobos repo so I can make a PR to fix this. Apparently I need to learn more git. :) -- Simen I'm already working on a PR. I think I can be ready by tomorrow. Sure, but mine's ready now :p https://github.com/D-Programming-Language/phobos/pull/4138 -- Simen
Re: foreach_reverse and lockstep.
On Friday, 1 April 2016 at 14:37:36 UTC, Jonathan M Davis wrote: (you'd need some sort of opApplyReverse function to go with foreach_reverse, and no such thing exists). Actually, it exists and it works. It's just not implemented for lockstep. I'm trying to clean up my Phobos repo so I can make a PR to fix this. Apparently I need to learn more git. :) -- Simen
Re: GC.malloc is pure - wat
On Friday, 1 April 2016 at 07:58:46 UTC, ZombineDev wrote: On Friday, 1 April 2016 at 05:00:43 UTC, ag0aep6g wrote: int[] f() pure nothrow {return [0];} [snip] The last case is different. It has nothing to do with pure. It is about reference-type literals and default field values: [snip] struct S { C c = new C; int[] arr = [1, 2, 3]; } No. Try this: int[] bar() { return [0]; } void main() { auto a = bar(); a[0] = 3; auto b = bar(); assert(b[0] == 0); } That assert passes with flying colors no matter the compiler flags. It is true that arrays in structs' initial state are copied (and may lead to subtle bugs), but the same is not the case for arrays returned from functions. -- Simen
Re: infer type argument in classe constructor?
On Tuesday, 29 March 2016 at 10:13:28 UTC, Puming wrote: Hi, I'm writing a generic class: ```d struct Message { ... } class Decoder(MsgSrc) { } ``` When using it, I'd have to include the type of its argument: ``` void main() { Message[] src = ...; auto decoder = new Decoder!(Message[])(src); ... } ``` Can it be inferred so that I only need to write? ```d auto decoder = new Decoder(src); // you can infer the type from src. ``` Nope. To see why, consider a class like this: class A(T) { T data; this(int n) { } } void main() { auto a = new A(3); // What is T? } The common solution is a simple 'create' function: Decoder!T decoder(T)(T msg) { return new Decoder!T(msg); } -- Simen
Re: [Blog post] Why and when you should use SoA
On Friday, 25 March 2016 at 01:07:16 UTC, maik klein wrote: Link to the blog post: https://maikklein.github.io/post/soa-d/ Link to the reddit discussion: https://www.reddit.com/r/programming/comments/4buivf/why_and_when_you_should_use_soa/ Neat. I've actually thought about writing exactly this kind of template for the fun of it. Thank you for showing how it'd work. Btw, your use of Tuple!ArrayTypes for the 'containers' field strikes me as unnecessary, as ArrayTypes on its own would cover all your use cases. -- Simen
Re: A Huge Bummer When Using alias this
On Thursday, 24 March 2016 at 21:26:00 UTC, Jack Stouffer wrote: alias this is only useful when not using any function that relies on the template constraints in std.traits. For example import std.traits; void someFunc(N)(N val) if (isNumeric!N) { int b = val; } void main() { import std.typecons; Nullable!int a = 42; someFunc(a); } $ dmd test.d test.d(13): Error: template someFunc cannot deduce function from argument types !()(Nullable!(int)) Removing the template constraint makes it compile and run just fine. What's a good work around for these types of issues? Simply put, isNumeric doesn't do what you think it does. It checks if the type is *exactly* one of the built-in numeric types (ints and floats of various known-at-compile-time sizes). Even BigInt, a part of Phobos that is explicitly supposed to work as a numeric thing, is not numeric accordion to isNumeric. IMO, isNumeric should be renamed isBuiltInNumeric or something to that effect, as it obviously does not test what it says it does. In addtion, there should be a function somewhat like numericBehavior below: template supportsBinOp(string op, T, U = T) { enum supportsBinOp = __traits(compiles, (T t, U u) { return mixin("t "~op~" u"); }); } template supportsUnOp(string op, T) { enum supportsUnOp = __traits(compiles, (T t) { return mixin(op~" t"); }); } template numericBehavior(T) { enum numericBehavior = supportsBinOp!("+", T) && supportsBinOp!("+", T, int) && supportsBinOp!("-", T) && supportsBinOp!("-", T, int) && supportsBinOp!("*", T) && supportsBinOp!("*", T, int) && supportsBinOp!("*", T) && supportsBinOp!("*", T, int) && supportsBinOp!("+=", T) && // Should opOpAssign be required? supportsBinOp!("+=", T, int) && supportsBinOp!("-=", T) && supportsBinOp!("-=", T, int) && supportsBinOp!("*=", T) && supportsBinOp!("*=", T, int) && supportsBinOp!("*=", T) && supportsBinOp!("*=", T, int) && //supportsUnOp!("++", T) && // Should these //supportsUnOp!("-+", T) && // be included? supportsUnOp!("+", T) && supportsUnOp!("-", T); } unittest { import std.typecons : Nullable; import std.bigint : BigInt; import std.meta : AliasSeq; import std.complex : Complex; alias goodTypes = AliasSeq!( byte, short, int, long, ubyte, ushort, uint, ulong, float, double, real, BigInt, Nullable!int, Complex!float ); foreach (e; goodTypes) { static assert(numericBehavior!e, "Expected "~e.stringof~" to have numeric behavior,"); } alias badTypes = AliasSeq!( string, int*, Object, void, Nullable!string, immutable int // Should this be on the list? ); foreach (e; badTypes) { static assert(!numericBehavior!e, "Did not expect "~e.stringof~" to have numeric behavior,"); } } This tests for behavior, not simply type. I'm not saying isNumeric is a bad template, it just has a confusing name.
Re: Does something like std.algorithm.iteration:splitter with multiple seperators exist?
On Wednesday, 23 March 2016 at 18:10:05 UTC, ParticlePeter wrote: Thanks Simen, your tokenCounter is inspirational, for the rest I'll take some time for testing. My pleasure. :) Testing it on your example data shows it to work there. However, as stated above, the documentation says it's undefined, so future changes (even optimizations and bugfixes) to Phobos could make it stop working: "This predicate must be an equivalence relation, that is, it must be reflexive (pred(x,x) is always true), symmetric (pred(x,y) == pred(y,x)), and transitive (pred(x,y) && pred(y,z) implies pred(x,z)). If this is not the case, the range returned by chunkBy may assert at runtime or behave erratically." But some additional thoughts from my sided: I get all the lines of the file into one range. Calling array on it should give me an array, but how would I use find to get an index into this array? With the indices I could slice up the array into four slices, no allocation required. If there is no easy way to just get an index instead of an range, I would try to use something like the tokenCounter to find all the indices. The chunkBy example should not allocate. chunkBy itself is lazy, as are its sub-ranges. No copying of string contents is performed. So unless you have very specific reasons to use slicing, I don't see why chunkBy shouldn't be good enough. Full disclosure: There is a malloc call in RefCounted, which is used for optimization purposes when chunkBy is called on a forward range. When chunkBy is called on an array, that's a 6-word allocation (24 bytes on 32-bit, 48 bytes on 64-bit), happening once. There are no other dependencies that allocate. Such is the beauty of D. :) -- Simen
Re: Does something like std.algorithm.iteration:splitter with multiple seperators exist?
On Wednesday, 23 March 2016 at 11:57:49 UTC, ParticlePeter wrote: I need to parse an ascii with multiple tokens. The tokens can be seen as keys. After every token there is a bunch of lines belonging to that token, the values. The order of tokens is unknown. I would like to read the file in as a whole string, and split the string with: splitter(fileString, [token1, token2, ... tokenN]); And would like to get a range of strings each starting with tokenX and ending before the next token. Does something like this exist? I know how to parse the string line by line and create new strings and append the appropriate lines, but I don't know how to do this with a lazy result range and new allocations. Without a bit more detail, it's a bit hard to help. std.algorithm.splitter has an overload that takes a function instead of a separator: import std.algorithm; auto a = "a,b;c"; auto b = a.splitter!(e => e == ';' || e == ','); assert(equal(b, ["a", "b", "c"])); However, not only are the separators lost in the process, it only allows single-element separators. This might be good enough given the information you've divulged, but I'll hazard a guess it isn't. My next stop is std.algorithm.chunkBy: auto a = ["a","b","c", "d", "e"]; auto b = a.chunkBy!(e => e == "a" || e == "d"); auto result = [ tuple(true, ["a"]), tuple(false, ["b", "c"]), tuple(true, ["d"]), tuple(false, ["e"]) ]; No assert here, since the ranges in the tuples are not arrays. My immediate concern is that two consecutive tokens with no intervening values will mess it up. Also, the result looks a bit messy. A little more involved, and according to documentation not guaranteed to work: bool isToken(string s) { return s == "a" || s == "d"; } bool tokenCounter(string s) { static string oldToken; static bool counter = true; if (s.isToken && s != oldToken) { oldToken = s; counter = !counter; } return counter; } unittest { import std.algorithm; import std.stdio; import std.typecons; import std.array; auto a = ["a","b","c", "d", "e", "a", "d"]; auto b = a.chunkBy!tokenCounter.map!(e=>e[1]); auto result = [ ["a", "b", "c"], ["d", "e"], ["a"], ["d"] ]; writeln(b); writeln(result); } Again no assert, but b and result have basically the same contents. Also handles consecutive tokens neatly (but consecutive identical tokens will be grouped together). Hope this helps. -- Simen
Re: How do I extend an enum?
On Saturday, 19 March 2016 at 17:40:27 UTC, Lass Safin wrote: Why: enum Base { A, B, } enum Derived : Base { C, // Gives error, says it can't implicitly convert expression to Base. D = 1, // Same error E = cast(Base)294, // Finally works. Can only be cast(Derived) instead. } void func(Derived d) {} func(Derived.E); // works. func(Derived.A); // Gives error, says it can't call function with Base.A. func(cast(Derived)Derived.A); // Works. So, what's the proper way of extending an enum? There is no way to extend an enum. When you think about it, it's actually the opposite of what you'd generally want. Given two classes: class A {} class B : A {} Every instance of B is a valid A. That is, given a variable of type A, you could assign any B to it. Now consider enums: enum A { x, y, z } enum B : A {} Which values could you put in B? Only those that would be valid for A. That is, only x, y and z. Imagine that we could: enum B : A { w } A foo = B.w; foo now holds a value that is not valid for its type. Hence, you simply cannot. Are there cases where you want to define a new enum that contains all the items in a 'base' enum in addition to some new items? Absolutely, and D lacks a good way to do that. But subtyping would in any case not be the correct way to do it. Are there cases where you want to extend an enum by making a subtype with more items? I would argue that's a strong code smell in D, but I can see why you'd want to. -- Simen
Re: Pattern matching as a library
On Sunday, 13 March 2016 at 02:33:49 UTC, Simen Kjaeraas wrote: http://dpaste.dzfl.pl/7360ee90b344 Dammit, 3:30AM was apparently too late, and some bad code leaked through. I managed to sidestep a problem by writing nonsense code. The problem I get can be reduced to this: struct S { void foo(alias a)() {} } unittest { S s; s.foo!((int i) => 1); // Works s.foo!(i => 1); // Fails } Result: foo.d(8): Error: template instance foo!((i) => 1) cannot use local '__lambda1' as parameter to non-global template foo(alias a)() Bah. It's in bugzilla as bug 5710[1] (with the most discussion), 3051, 3052, 11098, 12285, 12576 and 15564, and has a $150 bounty on bountysource: https://www.bountysource.com/issues/1375082-cannot-use-delegates-as-parameters-to-non-global-template Maybe it's time I learnt how DMD is put together and earn $150... Now, there's a way to work around that, by putting the lambda in an intermediate type. Sadly, that runs afoul of bug 15794[2] for the typed lambda, so I'm stumped for now. [1]: https://issues.dlang.org/show_bug.cgi?id=5710 [2]: https://issues.dlang.org/show_bug.cgi?id=15794
Re: Pattern matching as a library
On Saturday, 12 March 2016 at 20:56:47 UTC, Jacob Carlborg wrote: On 12/03/16 14:12, Simen Kjaeraas wrote: As I once again bemoaned D's lack of pattern matching yesterday, I was inspired to create this[0] implementation, that plays to D's strengths, allows for user-defined matching, and has a fairly usable syntax. The core usage looks like this: unittest { auto a = tuple(1, "foo"); auto b = match(a) ( _!(int, "foo") = (int i) => 1, _!(_, _) = () => 0 ); assert(b == 1); } What kind of syntax is that? Is "match" returning a struct with opCall that is called immediately? Indeed. The goal was to make it look similar to a switch statement. I actually started out with an idea for expanding switch with pattern matching using lowerings, then noticed I could do most of the stuff I wanted without compiler changes. With the user-defined matching implemented as follows: struct Tuple(T...) { // Implementation // Magic happens here bool opMatch(Pattern, Args...)(Pattern p, ref Args args) { foreach (i, e; p.pattern) { static if (isTypeTuple!e) { enum n = countTypes!(p.pattern[0..i]); args[n] = fields[i]; } else static if (!ignore!e) { if (fields[i] != e) { return false; } } } } } Is the tuple iterating all patterns to see if there's a match? Shouldn't that be the job for the the match function? The match function goes through the list of patterns and for each one asks the tuple if opMatch returns true for that pattern. If it does, the function assigned that pattern is called with the values assigned to args. opMatch here is checking for each element of the pattern if it matches the corresponding element of the tuple. Since the pattern is available at compile-time, opMatch can deny patterns it doesn't like (e.g. trying to match a Tuple!(int, string) with a string). The match function is really only a framework for having similar matching syntax for dissimilar types. If the capability of matching patterns to types were in the match function, how could a user type override it? Matching on a Tuple!(string, string) is different from matching on an Algebraic!(int[], Foo*) is different from matching on a specialized user type that wants to do something real weird (I'm not sure what that'd be, but I'm sure there are people who will want to). I've started implementing a pattern matching function as well. It has a syntax that only use compile time parameters, because both types and values can be passed. I'm not entirely sure on the syntax yet. I'll have to see what's possible to implement. Some suggestions: auto a = tuple(1, "foo"); auto b = match!(a, int, "foo", (int i) => 1, _, _, () => 0, ); That works. I feel the grouping is looser than in my example, and that the pattern doesn't stand out from the rest of the expression, but it certainly works, and there are some benefits to that syntax. If the pull request for inspecting templates ever will be merged it won't be necessary to have typed lambdas: auto b = match!(a, int, "foo", (i) => 1, _, _, () => 0, ); There's a problem using that syntax? It works for me in a toy example: http://dpaste.dzfl.pl/7360ee90b344 Sorry about the lack of comments and stuff, but it's 3:30AM, and I probably shouldn't be programming now.
Pattern matching as a library
As I once again bemoaned D's lack of pattern matching yesterday, I was inspired to create this[0] implementation, that plays to D's strengths, allows for user-defined matching, and has a fairly usable syntax. The core usage looks like this: unittest { auto a = tuple(1, "foo"); auto b = match(a) ( _!(int, "foo") = (int i) => 1, _!(_, _) = () => 0 ); assert(b == 1); } With the user-defined matching implemented as follows: struct Tuple(T...) { // Implementation // Magic happens here bool opMatch(Pattern, Args...)(Pattern p, ref Args args) { foreach (i, e; p.pattern) { static if (isTypeTuple!e) { enum n = countTypes!(p.pattern[0..i]); args[n] = fields[i]; } else static if (!ignore!e) { if (fields[i] != e) { return false; } } } } } Or for Algebraic: struct Algebraic(T...) { union { T fields; } size_t which; bool opMatch(Pattern, Type)(Pattern p, ref Type args) if (staticIndexOf!(Type, T) > -1) { enum index = staticIndexOf!(Type, T); if (index == which) { args = fields[index]; return true; } return false; } } The main problem I see is the temporary allocation of function arguments on line 124 and their assignment in opMatch, but I currently don't have a better solution. Also, while I very much dislike using _ for an identifier, I feel it may be the best alternative here - it conveys the meaning of 'don't care' for the pattern, and doesn't stand out like a sore thumb before the exclamation mark. Other suggestions are welcome. The code is available here, and I encourage everyone to play with it and critique: [0]: https://github.com/Biotronic/Collectanea/blob/master/biotronic/pattern.d
Re: Members as first class citizens!
On Saturday, 27 February 2016 at 18:48:27 UTC, Patience wrote: Ok, maybe not but this is what I mean: Why can't we pass member as as sort of "objects" in there own right to be used for accessing objects? e.g., class A { int? foo; A Parent; T GetAncestorValue(member field) // member is a new keyword { var p = this; while (!p && !p.field.HasValue) { p = p.Parent; } return p.field.Value; } } (This is pseudo D/C# code) Then auto x = a.GetAncestorValue(A:foo) would the properly initialized x. The code is simple, logical, and makes sense(since foo is just an "offset" and a type. It has type safety and doesn't resort to reflection and passing members as strings, etc. It allows for general access of members rather than having to jump through a bunch of hoops. It should be much faster too. Is there any fundamental theoretical reason why such a semantic could not be implemented in current object oriented compilers? There is absolutely no technical reason, no. C++ actually has this feature. The reason it has not been implemented in D is it's a (very) rarely used feature in other languages and perfectly possible to implement type-safely and efficiently in a library: struct nullable(T) { T value; bool hasValue = false; } class A { nullable!int foo; A parent; auto GetAncestorValue(T...)(Member!T field) { auto p = this; while (p && !field(p).hasValue) { p = p.parent; } return field(p).value; } } struct Member(T, U) { private int fieldId; @disable this(); private this(int id) { fieldId = id; } auto opCall(T that) { foreach (i, e; __traits(allMembers, T)) { static if (is(typeof(__traits(getMember, that, e)) == U)) { if (i == fieldId) { return __traits(getMember, that, e); } } } assert(false); } } template member(alias m) { import std.typetuple : TypeTuple; alias parentMembers = TypeTuple!(__traits(allMembers, __traits(parent, m))); template memberIndex(int n) { static if (parentMembers[n] == __traits(identifier, m)) { enum memberIndex = n; } else { enum memberIndex = memberIndex(n+1); } } enum member = Member!(__traits(parent, m), typeof(m))(memberIndex!0); } void main() { A a = new A(); A b = new A(); a.parent = b; b.foo.hasValue = true; b.foo.value = 3; a.foo.value = 15; assert(a.GetAncestorValue(member!(A.foo)) == 3); } Now, you lose the 'p.field' sugar, and it's possible the built-in feature could drop some safeguards in release mode to make it more efficient, but this should cover most of your concerns. -- Simen
Re: extern(C++, ns)
On Wednesday, 20 January 2016 at 12:47:44 UTC, Manu wrote: 2. Multiple modules cannot have the same name in D. I'm not sure what situation you're imagining where modules would be created with the same names...? How do you plan to map C++'s standard lib ? One giant hundred of thousands of line C++ file ? Surely it would map by file. #include -> import stl.vector; #include -> import stl.map; #include -> import stl.string; Perhaps 'stl' may be something else, 'std' is an unfortunate conflict with phobos, but this is unique to this case. I believe deadalnix here meant something like this: import stl.vector; import mylib.vector; Vector a; // Ambiguous: could be from mylib or stl stl.Vector b; // Error: no such class stl.Vector stl.vector.Vector c; // Works, but requires extra typing Which with working namespaces might allow case b to work. -- Simen
Re: extern(C++, ns)
On Wednesday, 20 January 2016 at 23:35:31 UTC, Manu wrote: On 21 January 2016 at 01:32, Simen Kjaeraas via Digitalmars-d <digitalmars-d@puremagic.com> wrote: On Wednesday, 20 January 2016 at 12:47:44 UTC, Manu wrote: 2. Multiple modules cannot have the same name in D. I'm not sure what situation you're imagining where modules would be created with the same names...? How do you plan to map C++'s standard lib ? One giant hundred of thousands of line C++ file ? Surely it would map by file. #include -> import stl.vector; #include -> import stl.map; #include -> import stl.string; Perhaps 'stl' may be something else, 'std' is an unfortunate conflict with phobos, but this is unique to this case. I believe deadalnix here meant something like this: import stl.vector; import mylib.vector; Vector a; // Ambiguous: could be from mylib or stl stl.Vector b; // Error: no such class stl.Vector stl.vector.Vector c; // Works, but requires extra typing Okay let me tweak your code: import stl.vector; import stl.string; // one more thing; when do you ever only import a single module? import mylib.vector; Vector a; // Ambiguous: could be from mylib or stl stl.Vector b; // Error: this wouldn't work, becase stl could be stl.vector.stl or stl.map.stl stl.vector.Vector c; // This is what a D programmer expects, exactly like every other conflict resolution in D ever, but it doesn't work either [snip] Which with working namespaces might allow case b to work. I hope I've shown how it almost never does, and only makes the situation worse. I'm playing devil's advocate here, since I fully agree with you that there is absolutely no need for C++ namespaces in D, and that the cost in language complexity is too high. Suppose that case b above worked, though - instead of a conflict, lookup would basically merge namespaces with the same name, and only conflict if there are multiple definitions that are not namespaces. On the one hand, I hope that's how it's supposed to work, otherwise it's just silly. On the other hand, that's a clear break with D's current lookup rules, and for what I consider very, very little benefit. -- Simen
Re: classInstanceSize and vtable
On Friday, 24 October 2014 at 00:21:52 UTC, Etienne Cimon wrote: On 2014-10-23 20:12, bearophile wrote: In D all class instances contain a pointer to the class and a monitor pointer. The table is used for run-time reflection, and for standard virtual methods like toString, etc. Bye, bearophile So what's the point of making a class or methods final? Does it only free some space and allow inline to take place? Like bearophile said the vtable is required for virtual methods. Consider this code: import std.stdio : writeln; class A { void foo() {writeln(A);} } final class B : A { override void foo() {writeln(B);} } void main() { A a = new B(); a.foo(); } In order for the call to foo to run the correct version of foo, B needs to have a vtable. Since all classes in D implicitly inherit from Object, which has some virtual methods, all classes need to have a vtable. -- Simen