Runtime error trying to call opCall on variant array of objects
Hey, so I'm trying to make an variant array of objects (each one has an opCall defined) and then call each of them in succession. It doesn't seem to be working. The error I get is: "Cannot apply `()' to a value of type `Command!(__lambda1, int)". I think it's failing when it checks for "!isFunctionPointer!A && !isDelegate!A". Here's the bit of code: struct Command(alias fun, Args...) { Args args; this(Args args) { args = args; } auto opCall() { return fun(args); } } auto command(alias fun, Args...)(Args args) { return Command!(fun, Args)(args); } void main() { auto commands = variantArray( command!(a => a + 1)(1), command!(a => a + 2)(1), command!(a => a + 3)(1), ); // commands.each!(a => a()); // <-- crashes runtime typeid(commands[0]).writeln; // std.variant.VariantN!32LU.VariantN auto cmd = command!(a => a + 1)(1); typeid(cmd).writeln; // scratchpad.main.Command!(__lambda4, int).Command } Is there a way to do what I'm trying to do? Cheers!
Re: Is there anything other than nullable to work with optional types?
On Monday, 26 December 2016 at 04:55:30 UTC, Seb wrote: Then help to push it forward!! There are many ways: - review the PR and point out anything problematic you see (lack of reviews/interest is a main reason why PRs get stalled) - post reasons and arguments for merging this PR (seems like you have a couple of good real world examples - post them!) - revive the PR (it's quite old and the author probably lost interest. There's absolutely nothing wrong with taking the diff and resubmitting it - on the contrary that's often the only possible way to revive/save old PRs) ... Sure, will take a gander. Might try and revive if I feel up to it after reading through but I don't think I'm comfortable enough with D yet to do that. But lets see.
Re: Runtime error trying to call opCall on variant array of objects
On Saturday, 24 December 2016 at 23:06:25 UTC, Ali Çehreli wrote: auto c = Command!(fun, Args)(args); writefln("Created %s", c); // (3) Workaround: Storing delegates, not Command instances. return () => c(); Ah, yes. Nice work around :) Thankies!
Re: Is there anything other than nullable to work with optional types?
On Sunday, 25 December 2016 at 20:11:50 UTC, Seb wrote: On Sunday, 25 December 2016 at 19:22:10 UTC, aliak wrote: or is there a way for it to not be so cumbersome to work with? You might be interested in https://github.com/dlang/phobos/pull/3915 Yes! That! Seems like that PR is on a break though :(
Is there anything other than nullable to work with optional types?
Hey, So, been using the programming language swift for a while now, the optional types[1] they support makes working with maybe-type (ala haskell) values extremely pleasant. Does D have anything other than the Nullable template that can be used to work with optionals, or is there a way for it to not be so cumbersome to work with? Eg: Currently I have a function like this: struct MarkerData { long start; long end; long length; long times; } Nullable!MarkerData pluckMarker(string str) { auto start = str.indexOf("("); if (start == -1) { return typeof(return).init; } auto end = str.indexOf(")", start); if (end == -1) { return typeof(return).init; } auto parts = str[start+1..end].split("x"); auto length = to!long(parts[0]); auto times = to!long(parts[1]); return Nullable!MarkerData(MarkerData(start, end, length, times)); } Everywhere I have to return the Nullable! type, I have to either use that typeof(return).init to make it a null value, or a quite verbose constructor call to return a concrete type. Can it be made simpler to use without me having to alias Nullable!MarkerData (which still wouldn't help with just returning null). [1] will try a super quick explanation of optional types. You basically add a '?' to any type declaration and then it's nullable. And you can implicitly assign nil and also the concrete type.
Re: Halp! type system (__expand_field_0 error), compile time/runtime questions (AoC-2017 puzzle spoilers inside)
On Thursday, 14 December 2017 at 15:28:22 UTC, aliak wrote: int[] rotate(int[] list, int[] lengths) { auto range = list.cycle; foreach (skip, length; lengths.enumerate) { // do stuff to range } return list; } Ok srsly, I got things to at least compile by changing the above rotate function to use a for loop instead lengths.enumerate: int[] rotate(int[] list, int[] lengths) { auto range = list.cycle; for (int skip = 0; skip < lengths.length; ++skip) { // do stuff to range } return list; } uhh ... ??
Halp! type system (__expand_field_0 error), compile time/runtime questions (AoC-2017 puzzle spoilers inside)
Warning: following has Advent of Code 2017 (day 14 and day 10) spoilers incase you are doing those challenges. And apologies for the slightly long post, I'm unsure how much information is needed here and am a bit clueless. Any help would be much appreciated. = Ok, so I'm trying to get through these daily code puzzles with D. Most of my troubles so far have come from trying to coax the type system in to behaving how I want/expect, and parsing input data at compile time. My background is a fair amount of C++ in the past, and these days Swift (just incase it helps with explaining things) (fun little tidbit, we're a decent size group at work doing these challenges in many languages and D is usually second in performance after C! :) - other contenders include python, go, scala, scheme, elixr, elm, javascript, fortran(!), kotlin, and quite a few more - one nut job actually decided to do a different language every day this month) So for executing each of these puzzles (they all have two parts) I have the following setup: app.d: import _day14; enum data = process(import("day14.txt")); // load input file void main() { solveA(data); solveB(data) } So input processing is compile time, and solutions are executed at runtime (i think?). Today's problem involves a custom hash function that was developed on day 10 of the calendar. So in my "_14.d" file I have this at the top: // takes a string, hashes it and returns 32 character hex string module _14; import _10: knotHashHex = solveB; Today's problem requires this hex string to be converted in to binary (i.e. each hex char to 4 binary chars) - I am aware that an optimization here would be to use bits instead of chars but meh :p. So I have this defined: // Takes "af49cb351fa..." -> "0010010111011..." alias knotHash = (string input) => knotHashHex(input) .map!binary // converts a dchar to a binary string so 'f' -> "" .join; So my app first calls "process" on the input data (which is a random string for today) and then the output of that goes in to solveA and solveB of my day14 module. My "process" function for today is this: // size "immutable size = 128;" // The puzzle requires a 128x128 grid of 1's and 0's so we reuse the input string // each round and just append a "-i" to it. auto process(string input) { return size .iota .map!(i => input ~ "-" ~ i.text) .array .map!knotHash } So the output type of this function is: MapResult!(function (string input) => join(map(solveB(input))), string[]) My solveA part works fine, you're supposed to count the number of 1's in the grid so it's a simple: auto solveA(ReturnType!process grid) { return grid .join .count!q{a == '1'}; } The next part is where I start to have problems. It involves a recursive depth-first-search approach so I have a function that recurses: void visit(ref string[] grid, int i, int j, ref bool[size][size] visited) { // check stuff and call visit again } And my solveB looks like this: auto solveB(ReturnType!process grid) { auto arrayGrid = grid.array; // convert to string[] // more code then at some point I call: visit(arrayGrid, i, j, visited); } So up to here everything works. But now I don't like that I have a "grid.array" inside solveB but not in solveA. Seems asymmetrical. So I figured I could just change my "process" function above to return an array directly by just adding a ".array" to the pipeline. Then I get this error: Error: couldn't find field __expand_field_0 of type ulong in Tuple(0LU, 117) 117 is the ascii code for 'u' btw, which happens to be the first character in my random input string. This error is coming from inside my day 10's "solveB" function (which is imported in day 14 as "knotHashHex"). It comes from here: int[] rotate(int[] list, int[] lengths) { auto range = list.cycle; foreach (skip, length; lengths.enumerate) { // <-- here is error // do stuff to range } return list; } So it says something about ulong and expanding a tuple type. But I'm having a hard time with this error message. Questions: What does it mean? It can't expand field 0 (the ulong) in to my skip variable? Why not? And most importantly, why is it having trouble here during my "process" function, but not screaming when I call "grid.array" in my "solveB" function? What am I doing wrong? So anyway, Halp!
Re: Halp! type system (__expand_field_0 error), compile time/runtime questions (AoC-2017 puzzle spoilers inside)
On Thursday, 14 December 2017 at 16:38:26 UTC, Steven Schveighoffer wrote: So enumerate returns as its element type a Tuple. Specifically, it's going to be a Tuple!(size_t, int), since you are enumerating an array of ints. I'm not sure why you are having the error, compiling your code above works perfectly fine for me. It would help to know: a) which version of the compiler you are using? Tried with dmd v2.077.1. Also with LDC 1.6 (which uses dmd v2.076.1) b) if the above actually does compile for you, what is the minimal code that does not? Yes of course, here's a minimal program that does not compile for me: import std.stdio, std.algorithm, std.range, std.array; int rotate(int[] lengths) { foreach(skip, length; lengths.enumerate) {} return 0; } auto knotHash(string input) { return [1].rotate(); } auto data = ["string"] .map!knotHash .array; void main() {} The above code, however, will compile with any of the following changes, and I don't understand why for any of them: 1) remove .enumerate 2) move the auto data inside the body of main 3) remove the call to .array All that aside, you may not realize, this works as well: foreach(skip, length; lengths) Sweet, thanks! Yeah that works too. Another thing I realized is that if I switch from .enumerate to the foreach you suggest (in my non minimized example) the compile time increases by A LOT. From about 1 second to 70 seconds.
Re: Halp! type system (__expand_field_0 error), compile time/runtime questions (AoC-2017 puzzle spoilers inside)
On Friday, 15 December 2017 at 01:43:04 UTC, Steven Schveighoffer wrote: So the CTFE interpreter doesn't like something to do with your chain of ranges. This is not totally unexpected, as the CTFE engine has lots of quirks that make it sometimes puke on valid CTFE-able code. :( At this time, I'd recommend doing your task at runtime anyway. Hopefully I have shed some more light on how things are working here. -Steve Hah yeah, I'll have to switch to runtime for quite a few of the tasks. But yes you've explained everything! thanks a lot for taking the time :) I guess I'll report the issue with enumerate since maybe it's something that should be fixed eventually.
Variable cannot be read at compile time.
Hi, I'm having a bit a trouble doing some compile time parsing. This works: immutable str = "he-.llo-the.re"; immutable separators = "-."; enum a = str.splitter(separators).array; But this does not: enum b = str.splitter!(a => separators.canFind(a)).array; The error is: cannot deduce function from argument types !((a) => separators.canFind(a))(immutable(string)) And this does: enum c = str.splitter!(a => a == a).array; And this does not: enum d = str.splitter!(a => a == separators).array; What am I missing? Thanks!
cannot deduce template lambda from argument
Hi, Having a little trouble understanding lambda type deduction. I have this lambda: immutable lambda(T) = (T n) => n * n; and if I call it with an explicit type it works else it errors with: lambda cannot deduce function from argument types !()(int) auto x = lambda!int(2); // ok auto x = lambda(2); // errors But if it's a normal template function then calling it without explicit type is ok. Thanks for any help!
Re: cannot deduce template lambda from argument
On Wednesday, 6 December 2017 at 08:10:26 UTC, Biotronic wrote: On Tuesday, 5 December 2017 at 23:01:43 UTC, aliak wrote: immutable lambda(T) = (T n) => n * n; Generally, you'd want to write alias lambda = n => n * n; instead. That said, I don't see any reason why your syntax shouldn't work, and would argue it's a bug. Please file it in Bugzilla. -- Biotronic Ok thanks! Done: https://issues.dlang.org/show_bug.cgi?id=18037 Btw, in the case of your suggested approach, what if I want to constrain the parameter type?
Re: cannot deduce template lambda from argument
On Wednesday, 6 December 2017 at 11:02:01 UTC, Jonathan M Davis wrote: If you only want one type, then given n that type; I'm pretty sure that it would be alias lambda = (int n) => n * n; if you wanted an int. But if you want to do anything more complicated with it, it would make more sense to just turn it into a proper function template. - Jonathan M Davis Roight, I was more thinking along the lines of a little more complicated I guess :) i.e.: template lambda(T) if (isIntegral!T) { alias lambda = (T n) => n * n; } But you're right, if it gets more complicated a proper function is probably better. Plus I just tried and it seems like you can't really constrain a variable template anyway. Unless there's some magic syntax I've overlooked in the docs. Thanks! Thanks!
Re: UFCS syntax I never saw before.
On Monday, 21 May 2018 at 14:19:35 UTC, Steven Schveighoffer wrote: On 5/21/18 8:15 AM, SrMordred wrote: Right, so this should´n be working I think. struct SomeStruct { void foo(int); } SomeStruct s; s.foo = 10; I thought that only with @property this will work. That was the plan, but it got derailed. Whoever wrote that original line of code, they need a stern talking-to. -Steve While wearing the naughty pointy hat and sitting in a corner :p
Re: UFCS syntax I never saw before.
On Monday, 21 May 2018 at 18:53:19 UTC, Jonathan M Davis wrote: writeln = "foo"; is legal, and it's dumb, but it hasn't mattered much in practice. So, causing a bunch of code breakage in order to disallow it is unlikely to go over well. It would also then make getters and setters inconsistent in that setters would require @property and getters wouldn't. How much that matters is debatable, but it does make such a change less palatable. [...] Can't assignment to a function be fixed though? Are there any cases where fixing that will cause problems for @property free functions because they all must take more that one parameter i assume. It's quite a big wart so we don't have to fix all of @property at least, but that should be fixed if fixing it does not crap on UFCS and @property free functions.
Re: UFCS syntax I never saw before.
On Tuesday, 22 May 2018 at 13:59:16 UTC, Steven Schveighoffer wrote: The derailed plan was to leave alone the ability to call no-arg functions without parentheses, but to REQUIRE @property to call an argument-taking function with the assignment style. See the DIP here: https://wiki.dlang.org/DIP23 Written by Walter and Andrei. I can't remember why it didn't happen. -Steve Aha. Thanks for the link! It feels like the only difference between a no-arg function that is @property and one that is not is that the former could be invoked with optional parentheses and the latter should be illegal with parentheses. Whereas an argument taking function marked as @property should probably allow read or write operations depending on whether or not it's invoked with an implicit first argument or not: @property int f(int) { ... } 1.f; // read op f = 1; // write op And to make parentheses illegal as well of course.
Re: Assigning a method name to a variable and then calling it with an object
On Thursday, 24 May 2018 at 23:08:29 UTC, Basile B. wrote: On Thursday, 24 May 2018 at 23:03:21 UTC, aliak wrote: Hi, I was essentially trying to do this: struct S { void f() {} } auto f = S.f; // f becomes void function(S) ?? S s; f(s); Is something like that possible? Cheers, - Ali Sure: ``` import std.stdio; void main(string[] args) { struct S { void f() {"yeah possible".writeln;} } void delegate() f; f.funcptr = S s; f.ptr = s.f(); } ``` It's just that you have to learn the ABI of D delegates. There are two members: .funcptr (function) and .ptr (context, i.e the "this"). ahh, gracias!
Re: UFCS syntax I never saw before.
On Tuesday, 22 May 2018 at 14:33:20 UTC, Jonathan M Davis wrote: A free function with a single argument works just fine as a setter property. e.g. you could do something like void env(Tuple!(string, string)[] str) { // set environment variables } env = [tuple("foo", "bar")]; is perfectly legal. I question that there are many cases where such a function would be considered good design, but basically any case where it would make sense to have a function act like a global variable is currently allowed but would be disallowed if you couldn't have a setter property with only one argument. - Jonathan M Davis That can be attributed with @property if the developer intends for it to be used in that way, else should be illegal.
Re: UFCS syntax I never saw before.
On Thursday, 24 May 2018 at 22:03:38 UTC, aliak wrote: It feels like the only difference between a no-arg function that is @property and one that is not is that the former could be invoked with optional parentheses and the latter should be illegal with parentheses. Edit: err... other way around!
Assigning a method name to a variable and then calling it with an object
Hi, I was essentially trying to do this: struct S { void f() {} } auto f = S.f; // f becomes void function(S) ?? S s; f(s); Is something like that possible? Cheers, - Ali
Re: UCFS does not work for nested functions?
On Monday, 18 June 2018 at 17:58:11 UTC, Steven Schveighoffer wrote: What then can happen is that your local calls can get hijacked from outside the module, if someone happens to define something later that you happened to import. D tries to avoid such possibilities. There's not much precedent for local symbols being overridden by module-level symbols. -Steve I thought that happens already with non-nested functions: module a; struct A { void f(); // assume it's added later } module b; import a; void f(A) { } void g() { auto x = A(); a.f(); // this would be calling local f until someone added A.f } Or I misunderstood what you said? Cheers, - Ali PS: This is something I've worried about before actually [1] when I was more of a noob than now, but I've come to accept I guess :) ... though I could still be misunderstanding things of course :/ https://forum.dlang.org/post/crcbaautgmrglhzvx...@forum.dlang.org
Re: UCFS does not work for nested functions?
On Monday, 18 June 2018 at 14:19:30 UTC, Steven Schveighoffer wrote: On 6/18/18 7:16 AM, Bastiaan Veelo wrote: On Sunday, 18 May 2014 at 08:15:08 UTC, Steffen Wenz wrote: Hi, Just noticed that using UFCS does not work for nested functions, and was wondering whether that's intended, and what the rationale behind it is: I just had the same question. I can imagine that the context pointer of nested functions complicates things, but making `bar` `static` does not help. Has anything changed in recent years regarding the difficulty of implementing UFCS for nested functions? Would it be easier to only support static nested functions? ``` void main() { static void bar(int x) {} int x; x.bar(); // Error: no property 'bar' for type 'int' } ``` It's never been supported, and likely will not be. I think the idea is that you can override expected behavior inside by accidentally defining some function locally with the same name. -Steve Wondering how this is different than with non-nested functions? If a global function has the same name as a member function then the member function takes precedence. So wouldn't the same thing just apply here if it were supported? Cheers, - Ali
Re: scope(success) lowered to try-catch ?
On Monday, 18 June 2018 at 12:48:46 UTC, Steven Schveighoffer wrote: On 6/17/18 11:58 PM, Neia Neutuladh wrote: [...] Yep, it's a good point. But also not the only way to do this. If you are returning void, just a goto would work: [...] I'm quite a noob when it comes to compiler stuff, and I see how this can be optimized when there're no exceptions, but I wonder what scope(success) actually means though without exceptions in play. It's just scope(exit) then right or? Cheers, - Ali
Re: How to list all the manifest constants in a class or struct
On Sunday, 17 June 2018 at 02:44:38 UTC, Heromyth wrote: Here is a struct named S: struct S { enum X = 10; enum Y { i = 10 } enum Z = "str"; struct S {} class C {} static int sx = 0; __gshared int gx = 0; shared void g(); } I want list all then the manifest constants in it. I searched the std.traits and this forums, but get nothing. Maybe, my real question is how to get the storage class for a member in a class or struct. Thanks. I think this bolts.isManifestAssignable [1] will get you partially there. The place where it'll fail though is a static immutable (since they are assignable to manifest constants) but you can filter those by seeing if you can take the address, something like: foreach (m; __traits(allMembers, T)) { if (isManifestAssignable!(T, m) && !is(typeof(mixin(""~m))) { // it's a manifest constant ... (?) } } There of course might be edge cases I can't think of/don't know about though. Cheers, - Ali http://bolts.dpldocs.info/bolts.traits.isManifestAssignable.html
Re: WTF! new in class is static?!?!
On Thursday, 7 June 2018 at 21:32:54 UTC, Jonathan M Davis wrote: struct S { int* ptr = new int(42); } Is that supposed to compile? -> https://run.dlang.io/is/SjUEOu Error: cannot use non-constant CTFE pointer in an initializer &[42][0]
Re: overload .
On Monday, 25 June 2018 at 13:37:01 UTC, Mr.Bingo wrote: One can overload assignment and dispatch so that something like A.x = ... is valid when x is not a typical member but gets resolved by the above functions. Therefore, I can create a member for assignment. How can I create a member for getting the value? A.x = 3; // Seems to get translated in to A.opDispatch!("x")(3) works but foo(A.x); // fails and the compiler says x does not exist I need something consistent with opDot. I am trying to create "virtual"(not as in function) fields and I can only get assignment but not accessor. A.x is translated in to A.opDispatch!"x" with no args. So I guess you can overload or you can static if on a template parameter sequence: import std.stdio; struct S { auto opDispatch(string name, Args...)(Args args) { static if (!Args.length) { return 3; } else { // set something } } } void main() { S s; s.x = 3; writeln(s.x); } Cheers, - Ali
Re: anyway to pass the context of an inner type to a template so it can be constructed?
On Wednesday, 27 June 2018 at 14:01:03 UTC, Alex wrote: On Wednesday, 27 June 2018 at 12:02:10 UTC, aliak wrote: === The use case is for a non-nullable type, where I want to guarantee that the value inside will never be null. I can't do it for inner classes though. And I can't allow the user to do something like: void main() { class C {} auto s = construct(new C); } Because I can't guarantee that's not null. Cheers, - Ali Is there any reason, why you don't want to use a struct? An instance of such is never null, still having access to its context, if it is a function. Sorry, by non-nullable I meant not null. It's that construct produces a wrapper type that has an internal value that I want to guarantee is not null. So whether T is a struct or class is dependent on the user of construct. - Ali
Re: anyway to pass the context of an inner type to a template so it can be constructed?
On Wednesday, 27 June 2018 at 19:28:37 UTC, Timoses wrote: Can't seem to avoid using mixin in main.. hehe yeah I see, didn't think of trying mixins, worth a shot! It seems like you had fun at least ;)
Re: overload .
On Monday, 25 June 2018 at 15:39:09 UTC, Mr.Bingo wrote: On Monday, 25 June 2018 at 13:58:54 UTC, aliak wrote: A.x is translated in to A.opDispatch!"x" with no args. So I guess you can overload or you can static if on a template parameter sequence: import std.stdio; struct S { auto opDispatch(string name, Args...)(Args args) { static if (!Args.length) { return 3; } else { // set something } } } void main() { S s; s.x = 3; writeln(s.x); } Cheers, - Ali Ok, for some reason using two different templated failed but combining them in to one passes: auto opDispatch(string name, T)(T a) auto opDispatch(string name)() Maybe it is a bug in the compiler that it only checks one opDispatch? Two opDispatchs as in: import std.stdio: writeln; struct S { void opDispatch(string name, T)(T t) { writeln(t); } auto opDispatch(string name)() { writeln("ret"); return 4; } } void main() { S s; s.x; s.x = 4; } ?? The above seems to work fine. Or maybe you meant something else?
Re: template sequence parameters treats member functions differently?
On Monday, 25 June 2018 at 15:06:42 UTC, Steven Schveighoffer wrote: On 6/24/18 5:19 PM, aliak wrote: [...] No, because the alias is an alias to the function, not the delegate. The act of taking the address creates the delegate, where the delegate's ptr is the context pointer (i.e. s), and the funcptr is the function that accepts the pointer (i.e. S.f). When you pass in s.f to an alias, you are actually passing in S.f. It's the fact that you are looking in the *namespace* of s when you do the alias. The is special for the compiler, and can't be deferred to later. Ahh, I see. Ah well. So not really much i can do here with this then I guess. Thanks for explaining though! BUT, I'm thinking this may be fixable, as it's inconsistent with inner functions: auto foo(alias x)() { return x(); } struct S { int bar() { return 42; } // int baz() { return foo!bar; } // nope } void main() { S s; int bar() { return 42; } assert(foo!bar() == 42); // ok // assert(foo!(s.bar) == 42); // nope int baz() { return s.bar; } assert(foo!baz() == 42); // ok! } I don't see any reason why the alias is to the function and not the contexted function. I don't see how it's any different from the ones which use inner functions. -Steve I can only agree - me no see either. And having no clue as to how the compiler is implemented, I cannot even conjecture :)
Re: template sequence parameters treats member functions differently?
On Monday, 25 June 2018 at 18:59:37 UTC, Steven Schveighoffer wrote: On 6/25/18 2:51 PM, aliak wrote: On Monday, 25 June 2018 at 15:06:42 UTC, Steven Schveighoffer wrote: I don't see any reason why the alias is to the function and not the contexted function. I don't see how it's any different from the ones which use inner functions. I can only agree - me no see either. And having no clue as to how the compiler is implemented, I cannot even conjecture :) Well, it's worth an enhancement request in any case. -Steve doneo: https://issues.dlang.org/show_bug.cgi?id=19026
template sequence parameters treats member functions differently?
Hi, I'm having some issues with template sequence parameters, it seems they are not typed as delegates inside a template, but are outside. I.e. template T(V...) { alias T = typeof([0]); } struct S { void f() {} } S s; pragma(msg, T!(s.f)); // void function() pragma(msg, typeof()); // void delegate() How come the output is different? Is it supposed to be the same? What I'm trying to do is write a template that can give me a tuple of types for all values of the sequence passed in, so that I can index in to the type tuple. Seems to work well except for this member function part, working source here: https://run.dlang.io/is/TBXHlY Specifically the commented out line is what I would like to be able to get working if possible. Thanks for any help, - Ali
Re: Wrapping a forward range in another forward range
On Sunday, 24 June 2018 at 20:33:32 UTC, Rudy Raab wrote: So I have an XLSX (MS Excel 2007+ file format) library that I wrote (https://github.com/TransientResponse/dlang-xlsx) that I recently converted from std.xml to dxml. That went well and it still works (much faster too). [...] I think it's the isSomeChar!(ElementType!R), not the isRandomAccessRange (because string isSomeString and !isSomeChar)? Cheers, - Ali
anyway to pass the context of an inner type to a template so it can be constructed?
This currently fails unless you mark the class as static: auto construct(T)() { return new T; } void main() { class C {} auto s = construct!C; } So wondering if there's anything that can be done to get the above working? Or if there isn't then how could the compiler be enhanced to allow for something like this if possible? === The use case is for a non-nullable type, where I want to guarantee that the value inside will never be null. I can't do it for inner classes though. And I can't allow the user to do something like: void main() { class C {} auto s = construct(new C); } Because I can't guarantee that's not null. Cheers, - Ali
what's the correct way to handle unicode? - trying to print out graphemes here.
Hi, trying to figure out how to loop through a string of characters and then spit them back out. Eg: foreach (c; "️") { writeln(c); } So basically the above just doesn't work. Prints gibberish. So I figured, std.uni.byGrapheme would help, since that's what they are, but I can't get it to print them back out? Is there a way? foreach (c; "️".byGrapheme) { writeln(c.); } And then if I type the loop variable as dchar, then it seems that the family empji is printed out as 4 faces - so the code points I guess - and the rainbow flag is other stuff (also its code points I assume) Is there a type that I can use to store graphemes and then output them as a grapheme as well? Or do I have to use like lib ICU maybe or something similar? Cheers, - Ali
Re: what's the correct way to handle unicode? - trying to print out graphemes here.
On Tuesday, 3 July 2018 at 13:32:52 UTC, aliak wrote: Hi, trying to figure out how to loop through a string of characters and then spit them back out. Eg: foreach (c; "️") { writeln(c); } So basically the above just doesn't work. Prints gibberish. So I figured, std.uni.byGrapheme would help, since that's what they are, but I can't get it to print them back out? Is there a way? foreach (c; "️".byGrapheme) { writeln(c.); } And then if I type the loop variable as dchar, then it seems that the family empji is printed out as 4 faces - so the code points I guess - and the rainbow flag is other stuff (also its code points I assume) Is there a type that I can use to store graphemes and then output them as a grapheme as well? Or do I have to use like lib ICU maybe or something similar? Cheers, - Ali Hehe I guess the forum really is using D :p The two graphemes I'm talking about (which seem to not be rendered correctly above) are: family emoji: https://emojipedia.org/family-woman-woman-boy-boy/ rainbow flag: https://emojipedia.org/rainbow-flag/
Re: How do you safely deal with range.front?
On Friday, 29 December 2017 at 20:47:44 UTC, Dukc wrote: On Friday, 29 December 2017 at 19:38:44 UTC, aliak wrote: So when I'm dealing with ranges, there're a number of times where I get the front of the returned result of a set of operations, but of course there is no front so you get an runtime access error. This could be what you want: auto makeInfinite(Range)(Range ofThis) { import std.range; return ofThis.chain(typeof(ofThis.front).init.repeat); } void main() { import std.stdio, std.range; auto testArray = [2, 3, 4].makeInfinite; foreach(e; testArray.take(5)) e.writeln; readln(); //stops the console from closing immediately; } /+ 2 3 4 0 0 +/ Hmm, interesting. Not sure that's what I'm looking for but I like it anyway :) I'm more looking to deal with situations like this: Instead of this: auto result = range.op!f; if (!result.empty) { result.front.method(); } This: range.op!f.ifFront.method(); In the above scenario I only want method to be called if the pipeline resulted in any element left in the range. On the other hand, I really do not recommend using ranges that way as a default. Most often you do not want to call front() or popFront() of an empty range in the first place. So if you end up doing so accidently, you want to know it. If it just returned some default value chances would be you never notice you have a bug. That's why front() in debug mode is usually programmed to termitate the program when it's called incorrectly. True you don't want to call front on empty, but sometimes I only care to call a method on front if it's there. Where the situation necessitates the existence of front, and not having a front is indeed a bug, then you want the program to terminate, yes. But not otherwise.
Re: How do you safely deal with range.front?
On Friday, 29 December 2017 at 20:11:03 UTC, Seb wrote: On Friday, 29 December 2017 at 19:38:44 UTC, aliak wrote: Hi, So when I'm dealing with ranges, there're a number of times where I get the front of the returned result of a set of operations, but of course there is no front so you get an runtime access error. [...] Do you know about the proposed `orElse`? https://github.com/dlang/phobos/pull/5154 Oh cool. No I did not. Seems like a nice approach. It would incur the creation of a new object when one doesn't exist, which I guess would be fine for many situations, and would work for this as well probably, but it'd be nice to avoid it as well in some situations. Just going thought std a bit now and I found this: https://dlang.org/library/std/typecons/black_hole.html That would be pretty cool to have range.op!f.blackHoleFront.call() - though blackHoleFront sounds horrible :p
Re: How do you safely deal with range.front?
On Friday, 29 December 2017 at 20:33:22 UTC, Jonathan M Davis wrote: Well, I don't know what you're really doing in code here, but in general, you'd write your code in a way that it checks empty and simply doesn't try to do anything with front if the range is empty. Yes, ideally, if programmers were perfect, this would work. The same applies to always checking null before dereferencing a pointer. You can of course try and make sure you always do check first. Or you can provide a safe way to do it in cases where you only want to dereference a pointer if the pointer is not null. Granted in some situations you want to crash as well because it would indeed be a bug if a pointer was null (or a range was empty). Btw, it seems that phobos, or at lest some of it, has an assert(!empty, "Attempting to fetch the front of an empty filter."). I guess probably it's not everywhere so hence the weird behavior you say happens sometimes :( ... ah well. You could wrap the range in a range that specifically returns some default element when the wrapped range is empty, meaning that the range would then be infinite. You could just use a wrapper function to call front and have it return a Nullable or a default value if the range is empty, but that wouldn't work when passing the range to other functions. You could also do something like an infinite range that simply returns the same value forever no matter how often front gets popped, and then you could use chain to combine your range and that range into a single range that would iterate through your range and then give the same element forever. Regardless, you'll have to be careful of any solution that involves creating an infinite range, since while some algorithms will be fine with it, others will either not compile or result in an infinite loop (e.g. don't use foreach on it). True, having to deal with infinite ranges will add to number of cases I'd have to worry about. Would prefer not to of course. I'm going to explore the Nullable approach you mentioned a bit me thinks ! Also, I found this now: https://forum.dlang.org/post/fshlmahxfaeqtwjbj...@forum.dlang.org . Seems to be what I'm looking for! Regardless, if you want to return an element when there isn't one, you're going to have to come up with a value for that. It's not something that's really going to work well generically. The generic constructs would occur ideally before accessing front. If not then yes, you're correct. Passing a "safeFront", or any other non-range value further down the pipeline would need a an undo function the other end of the pipeline.
Re: How do you safely deal with range.front?
On Sunday, 31 December 2017 at 01:03:17 UTC, Tony wrote: For me, front() should throw a pre-defined exception when called on an empty range in order to eliminate undefined behavior. It does take some time to make a check, but D does array bounds checking by default. Ideally the front() check could be turned off somehow ("-boundschecks=off") by the user for those who want maximum speed, but I guess there is no way to do that when using pre-compiled functions in a library. That sounds like a good idea. Wouldn't the same apply to array bounds checking for precompiled functions though? Also, is going out of array bounds well defined behavior in D even with boundscheck off? And any links to docs on UB in D?
Re: Converting array in to aliased tuple type.
On Monday, 25 December 2017 at 14:08:08 UTC, Mengu wrote: since Point is a Tuple and does not have a constructor that takes a list of integers (int[]), you should have a helper function. Aukay :( I was kind of hoping for some magical D variadic alias template on Tuple or something that will just deconstruct the arguments in to tuple components.
Re: Does to!(string)(char[]) do any memory allocation on conversion?
On Monday, 25 December 2017 at 14:12:32 UTC, Marc wrote: Does to!(string)(char[]) do any memory allocation on conversion or is this similar to a cast or what else? As said it calls idup, which calls _trustedDup which seems to call _dup which does memory allocation -> https://github.com/dlang/druntime/blob/v2.077.1/src/object.d#L3863 I think you can use assumeUnique to avoid allocs though. See code gen here: https://godbolt.org/g/44pLpL
How do you safely deal with range.front?
Hi, So when I'm dealing with ranges, there're a number of times where I get the front of the returned result of a set of operations, but of course there is no front so you get an runtime access error. In some other languages the concept of "front" or "first" returns a safe referece, or optional value that calling methods on will not crash the program. I'm thinking of maybe making something like safeFront(R)(R r) { return r.empty ? SafeRef!(ElementType!R) : SafeRef(r.front); } Where SafeRef, I think, can provide an overload for opDispatch and just forward the calls to the stored reference of r.front. If the stored reference is null then it can return a SafeRef to the return value of whatever was being dispatched to. Is there another way to go about this? Maybe through naturally using r.front instead of making a r.safeFront alternative? Cheers
static if and early exit from function doesn't seem to work?
Alo! I'm making a recursive concat function that is similar to chain. The following code works: import std.range: isInputRange; auto concat(R, V...)(R range, V values) if (isInputRange!R) { import std.range: chain, ElementType; static if (V.length) { static if (isInputRange!(V[0])) { return range.chain(values[0]).concat(values[1..$]); } else static if (is(V[0] == ElementType!R)) { return range.chain([values[0]]).concat(values[1..$]); } // add an else assert here. } else { return range; } } But the following does not: auto concat(R, V...)(R range, V values) if (isInputRange!R) { import std.range: chain, ElementType; static if (!V.length) { return range; } static if (isInputRange!(V[0])) { return range.chain(values[0]).concat(values[1..$]); } static if (is(V[0] == ElementType!R)) { return range.chain([values[0]]).concat(values[1..$]); } } You get a: Error: tuple index 0 exceeds 0 Error: template instance range.concat.concat!(Result) error instantiating So it seems it tries to compile the statements below the check on V.length even though it's guaranteed to be true and there's a return statement inside the if. Is it a current limitation of static if? or a bug? or is something like this just not possible because of something I'm not seeing? Cheers
Converting array in to aliased tuple type.
Hi, been looking for a way to convert an array to a tuple, but can't seem to find one. Is there one? Looking for something like: alias Point = Tuple!(int, "x", int, "y"); enum data = "1,2:8,9"; auto points = data .split(':') .map!(a => a .split(',') .map!(to!int) ) .map!Point; // <-- this works if you do `.map!(a => Point(a[0], a[1]));` instead Cheers!
Re: Define enum value at compile time via compiler argument?
On Monday, 25 December 2017 at 16:38:32 UTC, Thomas Mader wrote: On Monday, 25 December 2017 at 16:22:11 UTC, Mengu wrote: is it a relative path? if so: pragma(msg, __FILE_FULL_PATH__.split("/")[0..$-1].join("/")); https://run.dlang.io/is/gRUAD6 Nice idea but it is an absolute path. :-/ Can you use the -J compiler flag to define a string import file? Then you can: string data = import("file.txt");
Re: __traits(isSame, TemplateOf ...) errors and some Idiomatic D questions
On Monday, 8 January 2018 at 23:03:46 UTC, H. S. Teoh wrote: On Mon, Jan 08, 2018 at 10:59:44PM +, aliak via Digitalmars-d-learn wrote: [...] onlineapp.d(61): Error: template std.traits.TemplateOf does not match any template declaration. And I use it like this: enum r1Sorted = __traits(isSame, TemplateOf!(R1), SortedRange); This seems unnecessarily complicated. What about this instead? enum r1Sorted = is(R1 : SortedRange!T, T...); Basically, what that is-expression means is: "Is R1 a template of the form `SortedRange!T` where T is some list of template arguments". Generally, using __traits directly is usually not recommended unless there's no other way to achieve what you want. T Wow nice! Super nice :D Thanks! And ah I see, I suppose the double underscore kinda hints at that as well?
Re: __traits(isSame, TemplateOf ...) errors and some Idiomatic D questions
On Monday, 8 January 2018 at 23:22:04 UTC, Seb wrote: On Monday, 8 January 2018 at 23:14:32 UTC, Seb wrote: Your problem is that `TemplateOf!(int[])` isn't defined. It should probably be changed to return `void`. https://github.com/dlang/phobos/pull/6016 Damn that's some fast turnaround! And thanks for the explanation as well :) Cheers
__traits(isSame, TemplateOf ...) errors and some Idiomatic D questions
Hi, trying to write some idiomatic generic D code and I'm a bit stuck with using the TemplateOf to check if a range is a SortedRange or not. A bit about the code, I'm basically rewriting https://dlang.org/library/std/algorithm/setops/set_difference.html but I want to do different things based on if the passed in ranges are sorted or not. The error I'm getting is this: onlineapp.d(61): Error: template std.traits.TemplateOf does not match any template declaration. And I use it like this: enum r1Sorted = __traits(isSame, TemplateOf!(R1), SortedRange); That's happening on line 61 and the weird thing is that I'm using TemplateOf exactly (i think) the same way on lines 28 and 29 and those do not error. The code is here: https://run.dlang.io/is/9fowuP If I substitute the isSame trait with compiles, then it works: enum r1Sorted = __traits(compiles, TemplateOf!(R1), SortedRange); pragma(msg, r1Sorted); // prints true Any help would be appreciated! And on a side note, if anyone can point out any things I'm doing wrong when it comes to using D optimally/properly then please do. Cheers! And thanks for any help! PS: 1) Is there a better way to check if a predicate is unary or binary? 2) Is there an idiomatic way to check if a range is sortable or is it just "is(typeof(sort(range)))" basically it?
Rewriting a c++ template to D (replacing iterator with ranges "properly")
It basically steps through in a stride and sets the checkpoints to false. C++: template void mark(It begin, It end, N step) { assert(begin != end) *begin = false; while (end - begin > step) { begin = begin + step; *begin = false; } } For D this is what I figured would be the way? void mark(R, N)(auto ref R range, N step) if ( /* 1 */ isIntegral!N /* 2 */ && isRandomAccessRange!R /* 3 */ && is(ElementType!R == bool) /* 4 */ && hasAssignableElements!R ) { range.front = false; while (!range.empty) { range.popFrontN(N); range.front = false; } } I have a more specific question too: 1) I've seen some phobos code checking for assignability like this: is(typeof(range.front = false)) ... is that an advantage of that over hasAssignableElements? Or is that just basically combining constraints 3 and 4 which I have above? 2) Say I wanted to restrict to only lvalue ranges passed in as inputs. Does that mean I use hasLvalueElements as a constraint or is remove the "auto" and just have a ref parameter sufficient? Cheers
Re: Rewriting a c++ template to D (replacing iterator with ranges "properly")
On Friday, 26 January 2018 at 14:16:04 UTC, aliak wrote: range.front = false; while (!range.empty) { range.popFrontN(N); range.front = false; } } Oops, this should be: while (!range.empty) { range.front = false; range.popFrontN(N); }
Re: getting member functions of a struct and Error: identifier expected following ., not this
On Wednesday, 24 January 2018 at 07:55:01 UTC, thedeemon wrote: On Tuesday, 23 January 2018 at 00:00:38 UTC, aliak wrote: [...] The struct defined inside a scope can mention variables defined in that scope (e.g. use them in its methods), so it needs a pointer to the place where those closed variables live. That's the main difference between such struct and a static one that cannot use those scope vars. I guess you're seeing that pointer as additional member. As for static foreach, when you write a simple foreach over some compile-time tuple (like in this case), it's unrolled at compile time similarly to "static foreach", the main difference is whether it creates a sub-scope for the loop body or not. "foreach" creates one, "static foreach" doesn't. Ah makes sense. Thanks!
Re: Voldemort type for mixin template.
On Thursday, 11 January 2018 at 08:56:11 UTC, ChangLong wrote: When I try add some sub type for struct with mixin template, seems there is no way to hidden the private type. Is there a way to hidden type from mix template like Voldemort type ? fake code: mix template TypeX () { alias This = typeof(this); static struct Unique { This* _ptr ; } static struct Helper { private Unique data; } alias TypeX = { alias PublicName = Helper ; } } struct Node { mixin TypeX!(); PublicName helper; } Hi, can you explain a bit more? The question is not entirely clear to me. Can you mixin a struct of type PublicName and just hide everything in there? mixin template TypeX() { struct PublicName { private alias This = typeof(this); private struct Unique { This* _ptr; } private Unique _data; alias _data this; } } void main(string[] args) { mixin TypeX; PublicName helper; helper._ptr.writeln; } Cheers
Can you introspect predicate arity and if it's an equality predicate?
Hi, so basically is there a way to: void func(alias pred = null, Range)(Range range) { // 1) check if pred(ElementType!Range.init, ElementType!Range.init) is equality // 2) check if isUnary!pred // 3) check if isBinary!pred } I think maybe the isUnary or isBinary may not work unless it takes an extra parameter that gives it some context, i.e. isBinary!(pred, T, U) where T and U are the parameter types. Currently I have this for an isUnary template isUnary(alias pred) { static if (is(typeof(pred) : string)) { enum isUnary = is(typeof(unaryFun!pred(0))); } else static if (is(Parameters!pred F) && F.length == 1) { enum isUnary = true; } else { enum isUnary = false; } } But that does not seem to work in this situation: isUnary!(a => a) // returns false It won't work if I do isUnary!"a(2)" for e.g, in the case that T is callable or isUnary!"a.x" in the case that "a" is a struct with a member x ... just to name a couple of cases. If I give context then all's good: enum isUnaryOver = is(typeof(unaryFun!pred(T.init))); // works isEqualityFunction Im not sure how to go about. So open to any ideas, or if there're already some traits or something I can use. I was thinking I could do: alias E = // some element type static if (isBinary!(pred, E) && binaryFun!pred(E.init, E.init) == true) { // is equality ... but the for double.init is NaN == NaN ? // and probably some other things I'm not thinking about. } Advice and/or suggestions? Cheers PS: the purpose of the isEqualityFunction is to determine whether I should use the predicate for sorting or for just for searching, so: f!"a < b"(range) // range is sorted and then operated on f!"a == b"(range) // range is not sorted and is operated on by leveraging pred
Re: Rewriting a c++ template to D (replacing iterator with ranges "properly")
On Friday, 26 January 2018 at 14:35:25 UTC, Meta wrote: On Friday, 26 January 2018 at 14:16:04 UTC, aliak wrote: 1) I've seen some phobos code checking for assignability like this: is(typeof(range.front = false)) ... is that an advantage of that over hasAssignableElements? Or is that just basically combining constraints 3 and 4 which I have above? Where did you see this? That's got to be some very old code; I can't think of any instance where you would not want to use `hasAssignableElements` instead. Seems to occur in https://github.com/dlang/phobos/blob/v2.078.1/std/algorithm/mutation.d eg: https://github.com/dlang/phobos/blob/v2.078.1/std/algorithm/mutation.d#L555
Re: Rewriting a c++ template to D (replacing iterator with ranges "properly")
On Friday, 26 January 2018 at 14:59:09 UTC, Simen Kjærås wrote: what is N here? You're declaring it to be an int value in the template<> definition, and then use it as a type in the function definition. Oops again :) Should've been typename N (where N is some integral type). Not exactly. range.front will assert after the last popFrontN (since the range is empty). Ya, sorry, realized this a bit after I posted. It's trying to combine 3 and 4 I think, but it fails because this is allowed in D: int a; a = false; Ah true, so it's more of a is(ElementType!R : bool) check? You'll want to pass the range as ref. hasLvalueElements checks if the elements have lvalue semantics Doh, of course. It's in the name even! import std.range; struct R { int[3] elements; int index; ref auto front() { return elements[index]; } void popFront() { ++index; } bool empty() { return index >= elements.length; } } unittest { assert(hasLvalueElements!(R)); auto a = R([1,2,3], 0); auto b = a; b.front = 4; assert(a.elements == [1,2,3]); assert(b.elements == [4,2,3]); } As we can see, b is a complete copy of a, and changing b does not change a. The exact same behavior would occur if a was passed by value to a function. -- Simen Thanks for the input!
Re: questions around mutating range algorithms, const, and return ref
On Tuesday, 30 January 2018 at 09:51:18 UTC, Ali Çehreli wrote: > [...] is trying to > [...] It's the same with C++: A type with a const member cannot have a compiler-generated assignment operator. Ok, that made it obvious :) 'const' as a member function attribute is meaningful: It makes the implicit 'this' parameter const. Same as a function parameter attribute in that regard. >>> [...] I can >> [...] as in move >> [...] the >> [...] point. > [...] constructed, > [...] I'm not happy that I can answer that question. At least I opened a bug about some part of the confusion recently. :) https://issues.dlang.org/show_bug.cgi?id=18036 Ali ah, ok. Gotcha, thanks again, Ali.
questions around mutating range algorithms, const, and return ref
Hello, I'm trying to write a function called "pull" that, given 2 ranges, "pull"s the values from range 2 out of range 1. I'm not sure if I'm doing it correctly though, and I have some questions so any help is appreciated. This is what I have: ref pull(R1, R2)(return ref R1 r1, R2 r2) { import std.algorithm, std.array, std.range; int numFound = 0; auto r = r1.save; ElementType!R1[] unfoundElements; foreach (e; r) { if (!r2.canFind(e)) { unfoundElements ~= e; } else { numFound++; } } moveEmplaceAll(unfoundElements, r1); r1.popBackN(numFound); return r1; } So my questions are: 1) So first of all, is there a function like this in phobos that I can use? From what I understand phobos is supposed to be nogc so mutating functions like these are usually in place ones? 2) Is there a way to avoid the extra "moveEmplaceAll" call towards the end and still get the same results? 3) Is it necessary to always import std.array (or std.range: empty, front) when doing work like this with range algorithms? (otherwise you get no property save when an array is used as a range) 4) is the .save necessary? My (initial) tests seem to show that it works without, but from my understanding of what save does, it feels like it's necessary. 5) return ref and -dip25 ... is that something that's going to become default at some time? Is there a timeline? (also curious about dip1000 for that matter). 6) It will not work when I pass in a range that has const elements. ie: const(int)[] arr = [1, 2, 3, 4, 5]; arr.pull([2, 3]); This I guess makes sense because moveEmplaceAll depends on isInputRange and from my understanding, a const range cannot be one (is that correct?). So am I correct in thinking there really is no way around this, or is there some move/cast trickery that I can use maybe? Cheers, - Ali
Re: Lambda returning a lambda?
On Friday, 2 February 2018 at 01:31:15 UTC, Seb wrote: Are you aware of partial? https://dlang.org/phobos/std_functional.html#partial Si si :) And now I'm thinking, practically, that might be enough. So thanks for the prod.
Can you constrain a type based on properties of what it is being referenced by?
To further explain what I mean: struct A if (!is(this == immutable) && !is(this == shared)) { } shared a = A() // error auto a = A() // ok immutable a = A() // error const a = A() // ok Fake syntax above of course. I was thinking about this because I read a few posts about const, and approaching it via allowing an object to define a head mutable version of itself (see post in general forum by Simon for details [1]) And there're few constructs like RefCounted which don't make sense with an immutable qualifier. And then there're situations where a mutable version does not make sense (I think this would be a much bigger application), ie: handles, resource identifiers, application configuration details, etc. [1] https://forum.dlang.org/thread/cpxfgdmklgusodqou...@forum.dlang.org
Re: Can you constrain a type based on properties of what it is being referenced by?
On Friday, 2 February 2018 at 14:19:37 UTC, aliak wrote: ... (see post in general forum by Simon for details [1]) *Simen Gah! Sorry!
Lambda returning a lambda?
Is there a way to do this: import std.stdio; void main() { alias f = (a) => (b) => a * b; f(2)(3).writeln; } Error now is: Error: template lambda has no type Cheers
Re: Rewriting a c++ template to D (replacing iterator with ranges "properly")
On Friday, 26 January 2018 at 23:15:41 UTC, Simen Kjærås wrote: The function is called fill, and assigns a value to every element in the range. If a[0] = false compiles, we also want a.fill(false) to compile. It's simply testing that, rather than caring about the exact type of the elements. -- Simen I see. Yes that makes sense. Thank you.
Re: questions around mutating range algorithms, const, and return ref
On Monday, 29 January 2018 at 06:46:26 UTC, Ali Çehreli wrote: I think the following trivial wrapper around std.algorithm.remove() should do: void removeMatching(R, N)(ref R r, N needles) { import std.algorithm : remove, canFind; r = r.remove!(e => needles.canFind(e)); } Haha awesome! Yes that's much better :) Note to self: learn to scroll down in the docs to find other definitions. I was convinced that remove only acted on indices :p I'm not convinced that unfoundElements is needed at all. :) Can't argue with that :) > 6) It will not work when I pass in a range that has const elements. > > ie: > > const(int)[] arr = [1, 2, 3, 4, 5]; > arr.pull([2, 3]); > > This I guess makes sense because moveEmplaceAll depends on isInputRange > and from my understanding, a const range cannot be one (is that > correct?). Yes but your range is not const, the elements are. So, although const(int)[] is a range, it does not have mutable elements. Ah right, yes the range is not const indeed. You don't want to mutate const elements anyway. It would be breaking a promise that other parts of the program may be depending on. I would return a filtered-out range: Right, I want to mutate the range though, not the elements. So moving things from one location is not considered as a const violation in c++ for eg, maybe the D way is different though? Any reading material there? Also hasMobileElements!(const int[]) is true, so that means I can move elements around right? If I can move elements, that means I should be able to move the ones I want to the beginning, of the range, but then I'm also unsure about how to go about changing the size of a range. popBack on a mutable range with const data might cause destructors to be called, so I'm going to say that should be ok because as a user, if you're calling a range that mutates by removing things, and if you try and access those things later that's probably along the same lines as removing elements from a range that you're "foreach"ing over. And your second approach is definitely more practical I'd say, but I'm going to go about this as a learning exercise in dealing with mutations, and const with ranges in D. Thanks you for the comments!
Re: Lambda returning a lambda?
On Thursday, 1 February 2018 at 14:28:34 UTC, Simen Kjærås wrote: -- Simen Ah, thank you both. For solution and explanation. Me wonders... are there any thoughts around having functions return aliases as well? I have no idea if it's even possible but if it is, then does the initial syntax become possible? Or some kind of lazy type deduction that just tries to determine type when actually needed, and everything is an alias until that point? Would a library curry solution on an alias lambda be possible? alias f = curry!((a, b) => a * b) ?
Re: questions around mutating range algorithms, const, and return ref
On Monday, 29 January 2018 at 13:55:04 UTC, Seb wrote: On Monday, 29 January 2018 at 11:36:26 UTC, aliak wrote: On Monday, 29 January 2018 at 06:46:26 UTC, Ali Çehreli wrote: I think the following trivial wrapper around std.algorithm.remove() should do: void removeMatching(R, N)(ref R r, N needles) { import std.algorithm : remove, canFind; r = r.remove!(e => needles.canFind(e)); } Haha awesome! Yes that's much better :) Note to self: learn to scroll down in the docs to find other definitions. I was convinced that remove only acted on indices :p Not too hard to fix: https://github.com/dlang/phobos/pull/6090 Nice! :D
Re: questions around mutating range algorithms, const, and return ref
On Monday, 29 January 2018 at 12:10:16 UTC, Simen Kjærås wrote: Consider this case: immutable(int)[] a = [1,2,3]; immutable(int)* b = [1]; You're free to slice the array or pop elements off its front or back, but if you change the order of elements, the value that b points to will change. D does not allow this. You can create a new array with the elements moved into the locations you want, and with another layer of indirections (int*[]) you can move the elements of the array without mutating the values. Ooh my... yes I can understand that. And I guess immutable is implicitly convertible to const and then if you can move const stuff then you have that problem. From the FAQ, my general understanding is that const is there as a bridge from immutable to mutable as a promise that the memory under this symbol will not be altered by that reference. While experimenting a bit I came across this though which, currently, I do understand that value of struct Int { const int value; } void main() { Int i0 = Int(0); Int i1 = Int(1); Int i2 = Int(2); Int[3] arr0 = [i0, i1, i2]; Int[] arr1; arr1 ~= i0; arr1 ~= i1; arr1 ~= i2; arr1[0] = i0; // nope, Error: cannot modify struct arr1[0] Int with immutable members } So if a struct has a struct that has any member const, then effectively that whole struct is a const (well immutable it seems, so not even const)? Is that really correct? What is this sorcery? If yes I find it very surprising, but I don't think I'm very clear on what D is trying to solve with const ... yet. I find const a little hard to swallow so far. From what I understand, it's good for function parameters, and as a function attribute for functions that return temporaries, except don't use it with ranges (i.e. don't put it on front). Actually not sure about function attributes on second thought. So... function parameters and local variables that you know won't change is what I'm going to go with for now. Also hasMobileElements!(const int[]) is true, so that means I can move elements around right? hasMobileElements checks if the value of front can be moved as in move constructors, not as in 'can be moved to a different spot in the range'. I will admit the name does little to unconfuse this point. I'm not following here :s If value of front can be move constructed, then something can take it's place no? Thank you!
free func "front" is not picked up as a candidate when doing range.front(...)
Hi, I'm trying to make a range.front free function that can be given a defaultValue. Doesn't seem to be working as is written below, seems like the compiler doesn't see the free function as a viable candidate. Isn't it supposed to do its UFCS wizardry and pick up the free func? import std.stdio; auto front(Range, T)(Range range, T defaultValue) { import std.range: empty, front; return range.empty ? defaultValue : range.front; } void main() { import std.range: iota; 0.iota.front(100).writeln; // Error: inout method std.range.iota!(int, int).iota.Result.front is not callable using a mutable object import std.algorithm: filter; auto arr = [0, 1]; arr.filter!"true".front(99).writeln; // Error: function std.algorithm.iteration.FilterResult!(unaryFun, int[]).FilterResult.front () is not callable using argument types (int) arr.front(30).writeln; // OK import std.range: front; arr.front(30).writeln; // Error: template std.range.primitives.front cannot deduce function from argument types !()(int[], int), candidates are: std.range.primitives.front(T)(T[] a) ... } Cheers
Re: free func "front" is not picked up as a candidate when doing range.front(...)
On Thursday, 8 February 2018 at 19:32:42 UTC, Jonathan M Davis wrote: On Thursday, February 08, 2018 08:18:20 aliak via Digitalmars-d-learn wrote: On Thursday, 8 February 2018 at 07:16:43 UTC, Jonathan M Davis wrote: > It would be a disaster if free functions could override > member functions. For starters, it would be impossible to > call the member function if that were allowed, whereas you > can always call a free function by not using UFCS. And in > general, it's far more desirable to have member function > functions override free functions, because then you can do > stuff like have have a range override find if it can provide > a more efficient implementation than std.algorithm.find. That could be fixed with a precedence rule though no? Then member find will be preferred over free find. And it won't be impossible to call member functions. That's exactly how it works now. If you call a function as if it were a member function, and that type has that member function, then it's that member function that gets called. If that type doesn't have that member function, then free functions are checked. Wait no, that's not how it works now. Right now if a member function with the same name exists, then free functions are not even checked. Whereas if there's a precedence rule that prefers member functions, then free functions will still be checked if no member function with that name can be deduced as a viable candidate. How it is now disregards free functions without any consideration (if I've understood correctly), whereas I'm asking why free functions are not even considered if a member function is not an actual candidate. I'm not asking about overriding member functions with free functions. i.e. in my original post, the free function front(int) does not exist in type FilterResult. Cheers, - Ali
Re: free func "front" is not picked up as a candidate when doing range.front(...)
On Wednesday, 7 February 2018 at 20:08:10 UTC, Paul Backus wrote: You can only call a function with UFCS syntax if the object you're calling it with does not have a member with the same name as the function. Both iota's `Result` type and `FilterResult` have properties named "front", so you can't call your front function on them using UFCS syntax. ahhh... bummer. Do you know if it is a limitation that is yet to be implemented or one that will not (cannot?) be implemented and if so what the reason is?
Re: free func "front" is not picked up as a candidate when doing range.front(...)
On Thursday, 8 February 2018 at 07:16:43 UTC, Jonathan M Davis wrote: It would be a disaster if free functions could override member functions. For starters, it would be impossible to call the member function if that were allowed, whereas you can always call a free function by not using UFCS. And in general, it's far more desirable to have member function functions override free functions, because then you can do stuff like have have a range override find if it can provide a more efficient implementation than std.algorithm.find. That could be fixed with a precedence rule though no? Then member find will be preferred over free find. And it won't be impossible to call member functions. Also, in this case where it's an overload, and not an override, it's very clear what should be called isn't it? In the case of an override I "feel" it may indeed be a disaster, but not for the reasons you've stated above though, also not for any reasons I can think of :) Hijacking maybe? But then again, if I have a free function and then decide to make a member function I'm hijacking the free function... hmm. Cheers
Re: free func "front" is not picked up as a candidate when doing range.front(...)
On Thursday, 8 February 2018 at 22:57:04 UTC, Jonathan M Davis wrote: D tends to be very picky about what it puts in overload sets in order to avoid function hijacking - e.g. it doesn't even include base class functions in an overload set once you've declared one in a derived class unless you explicitly add an alias to the base class function in the derived class. Also, D doesn't support "best match" with function overloads. The matches have to be exact - e.g. as soon as implicit conversions come into play, there can only be one match. If there's ever a conflict, you get a compilation error (unlike in C++, which tries to figure out what it thinks the best match is and go with that, sometimes with surprising results). And having both the member functions and free functions in the same overload set would basically require the compiler to go with the "best match," which simply isn't how D deals with overloads. Sometimes, that's annoying, but it also prevents a lot of subtle bugs that tend to crop up in C++ code. https://dlang.org/spec/function.html#function-overloading https://dlang.org/articles/hijack.html - Jonathan M Davis Thanks for the articles, they shed a bit more understanding. But I have some concerns/issues that maybe you or others can help out with a bit more. First, the link on function-overloading says "The function with the best match is selected. " So I'm not sure what you mean about subtle bugs in C++ and D doesn't support best match. If you have foo(long), food(int) and call foo(3) in the same module, the best match is selected in D and in C++. But, anyway, ignoring C++ for now, after reading those articles you linked to my main issues are D related, not C++, let me try to explain: The rules of resolving overload sets seem to be to resolve the issue where two overload sets have a matching function and one is silently better. This is understandable and makes sense. The case of a free function not being considered if there is a member functions seems unrelated (as you say they are not part of the overload set, but it seems like like maybe they should be). In this case the member function does not match. If overload set resolution was applied the free function would be considered and be chosen as the only available candidate. The rules for overriding, where functions with the same name in a base class are hidden, also make sense. But not relevant here. Infact, the hijacking article makes it seem to me that ufcs hijacking has maybe been overlooked? Consider two modules which are unrelated: module a; string f(R)(R r, int i) { return "a.f"; } string g(R)(R r) { return "a.g"; } - module b; auto algo() { static struct R { void popFront() {} enum empty = false; int front() { return 1; } } return R(); } -- // Application programmer; import std.stdio: writeln; import a: f, g; import b: algo; void main(string[] args) { auto r = algo(); auto a = r.f(3); auto b = r.g; writeln(a); // will print a.f writeln(b); // will print a.g } All good so far. Until module b decides to add an internal function to it's R type: auto algo() { static struct R { // Add a function string g() { return "internal g"; } } return R(); } Application programmer updates module b... auto b = r.g; // Does not do what programmer thinks anymore, and no warnings. Because a.g does not participate in overload set resolution rules, the application programmer has an unknown bug (is it just me or is this a bit... to put it lightly... scary?) So that's the first problem. Then, secondly, say the people who make module b like using proper access control levels, and mark the internal string g() as private. Now there's a compiler error in the application programmer, with no real clues as to why, unless you deep dive in to D lang specs. And then you find out you're getting a compiler error because of a private, internal, function in a Voldermort type... ouch. Like. Big ouch. But ok, this can at least be worked around: import a: f, c = g; Now: auto b = r.c; // ok. Third problem: Module b person adds another internal function to their type. auto algo() { static struct R { // Add a function string f() { return "internal f"; } } return R(); } Now application programmer has a compile error again. Again with no clue. But this time it makes no sense because the line: auto a = r.f(3); Is clearly and unambiguously calling a.f(int). Again here if the free function was part of the overload set resolution rules, it would be resolved correctly. Granted problem two and three are related. But there're subtle semantic difference there. Either this has been overlooked or there's a reason it is like this and must be like this that I've yet to hear. As it is right now (someone correct me if I'm wrong), for someone who is writing
Re: opUnary with ++ and -- on a struct that has a dynamic array
On Monday, 12 February 2018 at 06:16:21 UTC, rumbu wrote: writeln(a++) translates to: A copy = a; a.opUnary!"++"; writeln(copy); copy.a[] and a.a[] are the same reference, you increment a.a[0]/copy.a[0] in opUnary to make this work you will need a postblit constructor: struct A { this(this) { a = a.dup; //make a copy a; } } In fact, you'll find exactly the same scenario in docs: https://dlang.org/spec/struct.html#struct-postblit Ah! Awesome. Yes that works, thank you! Cheers
Re: opCast cannot implicitly convert a.opCast of type X to Y
On Tuesday, 13 February 2018 at 12:12:30 UTC, Nathan S. wrote: On Monday, 12 February 2018 at 02:05:16 UTC, aliak wrote: struct B(T) { T t; } struct A(T) { T t; auto opCast(U)() { return B!U(cast(U)t); } } void main() { auto a = A!int(3); auto b = cast(float)a; // error } Having the result of "cast(float) a" not be a float would be evil. Ya :p dunno what I was thinking.
opCast cannot implicitly convert a.opCast of type X to Y
From spec: Cast expression: "cast ( Type ) UnaryExpression" converts UnaryExpresssion to Type. And https://dlang.org/spec/operatoroverloading.html#cast makes no mention of the return type of opCast. One could think that the return type of opCast would be the return type. But it seems it must be the same as the template parameter of opCast else you get a compile error that seems like it can be much better. --- import std.stdio; struct B(T) { T t; } struct A(T) { T t; auto opCast(U)() { return B!U(cast(U)t); } } void main() { auto a = A!int(3); auto b = cast(float)a; // error } Error: cannot implicitly convert expression a.opCast() of type B!float to float Is this deliberate? The use case I have is making an optional type that you can cast to a different type: auto opCast(U)() const { static if (isOptional!U) { alias V = OptionalTarget!U; return empty ? no!V : some!V(cast(V)front); // it's a range so "front" is the raw value } else { return empty ? no!U : some!U(cast(U)front); } } It would allow for scenarios like: Optional!int a = 3; auto b = cast(float)a; // b == some!float Cheers - Ali
opUnary with ++ and -- on a struct that has a dynamic array
Hi, Is there a way to get post increment and pre increment working properly in this scenario? import std.stdio; struct A { int[] a; this(int a) { this.a = [a]; } auto opUnary(string op)(){ return A(mixin(op ~ "this.a[0]")); } } void main() { auto a = A(0); int b = 0; writeln(a++, ":", b++); // 1, 0 <-- wrong post increment result writeln(++a, ":", ++b); // 2, 2 } If I switch the order of the mixin expression (i.e. "this.a[0]" ~ op) then the pre increment does not work. Any tips? Cheers - Ali
Re: opCast cannot implicitly convert a.opCast of type X to Y
On Wednesday, 14 February 2018 at 15:14:24 UTC, Meta wrote: I think the best way to do this is to implement `map` for your optional type. Optional!U map(U, alias f)() { return empty? no!U : some!U(f(t)); } Optional!int a = 3; auto b = a.map!(v => cast(float)v); assert(is(typeof(b) == Optional!float)); Ooh yes, of course! Thank you :)
Re: opCast cannot implicitly convert a.opCast of type X to Y
On Thursday, 15 February 2018 at 00:34:33 UTC, Meta wrote: On Thursday, 15 February 2018 at 00:27:40 UTC, Meta wrote: On Wednesday, 14 February 2018 at 23:46:30 UTC, aliak wrote: On Wednesday, 14 February 2018 at 15:14:24 UTC, Meta wrote: Ooh yes, of course! Thank you :) Even better: import std.conv; auto b = a.map!(to!float); Actually, that won't quite work without redefining map a little: Optional!U map(alias f, U = typeof(f(t.init)))() { etc... } Ah yes, true, also auto return would work. But then you'd still need to do the typeof(f(T.init)) evaluation in the body... plus you lose being able to see an explicit return type i guess... hmm. So nevermind :) Though a free function would be good me thinks. Then you could use it seamlessly with std.algorithm.map. Optional!U map(alias f, T, U = typeof(f(T.init)))(Optional!T opt) { return Optional!U(f(opt.t)); } Cheers, - Ali
Re: Trying to forward unwrapped opDispatch names to alias this
On Monday, 19 February 2018 at 01:00:23 UTC, Adam D. Ruppe wrote: On Monday, 19 February 2018 at 00:42:05 UTC, aliak wrote: struct B(T) { T t; A a; alias a this; auto opDispatch(string name)() if (hasMember!(T, name)) { return mixin("t." ~ name); Did you perhaps mean `A` instead of `T` here? cuz in your code T is int, not the struct. I don't think I did :p T is the wrapped type. So if T has a member (in the example it's the built in field "max") then forward that. If member not there then I figured the alias this would be used. I.e. in the example b.p should call A.p. I assume this should work because rules for alias this (as I understand) are to basically try if there's a member name that resolves the call, else forward to alias this.
Trying to forward unwrapped opDispatch names to alias this
I have a scenario where I'm wrapping functionality for a type, but only if the contained type has a member. I want those to take precedence. If the member is not there, then I want to delegate to an aliases type via alias this. I get an error here when I call b.p. Even though property p is in type A and there's an alias a this in B. Anyway to do this? struct A { @property string p() { return "A"; } @property void p(string str) {} } struct B(T) { T t; A a; alias a this; auto opDispatch(string name)() if (hasMember!(T, name)) { return mixin("t." ~ name); } } void main() { B!int b; b.max.writeln; b.p.writeln; // Error: no property 'p' for type 'B!int' } Cheers, - Ali
Re: Trying to forward unwrapped opDispatch names to alias this
On Tuesday, 20 February 2018 at 11:27:23 UTC, Alex wrote: There is a related ticket, https://issues.dlang.org/show_bug.cgi?id=6434 However, not exactly facing this question. Should that ticket be marked as resolved? The issue is for alias this to be considered before opDispatch but there were no arguments after giving reasons for that being a bad idea. And opDispatch is considered before (and disables alias this) now it seems? It seems, that currently opDispatch replaces alias this... (?) Nevertheless, not only T is the wrapped type, but also A with the alias. What to do, if both members have a property with the same name? And why? Good question, should it be dealt with in the same way as this is dealt with? struct S { void f() {writeln("S");} } struct A { S s; alias s this; void f() {writeln("A");} } A().f; // prints "A" However, the above behavior seems dangerous maybe and could lead to silent bugs. I'm thinking if A.f was not defined, then A().f would print "S". But then someone comes along and defines an A.f and the user of A now gets different functionality without even knowing about it. Hrm.. one is not sure how one feels :p But how about if they do not have the same name? Currently AliasThis says: "If the member is a class or struct, undefined lookups will be forwarded to the AliasThis member." So opDispatch, even though it's constrained, is not considered undefined if it doesn't pass. In those cases should it not be forwarded to the alias this? If not, then why does a constrained function not qualify as being undefined if the constraint doesn't pass? Consider: struct A { void defined() {} } struct B { void opDispatch(string name)() if (name == "defined") {} } void main() { A().defined; // ok B().defined; // ok A().undefined; B().undefined; } Errors are: * Error: no property 'undefined' for type 'A', did you mean 'defined'? * Error: no property 'undefined' for type 'B' If you then define a struct S that contains a function named "undefined" and add a "S s; alias s this;" inside A and B, you get: * Error: no property 'undefined' for type 'B' But not for A. Shouldn't the behavior here be consistent? Or why not? Churr, - Ali
Re: Trying to forward unwrapped opDispatch names to alias this
On Tuesday, 20 February 2018 at 16:12:17 UTC, Adam D. Ruppe wrote: On Monday, 19 February 2018 at 08:28:22 UTC, aliak wrote: T is the wrapped type. So if T has a member (in the example it's the built in field "max") then forward that. Oh, I see what you mean. So the problem is that built in types don't have "members" per se, they have "magic". The built in properties don't count to `hasMember`. You could probably do `if(__traits(compiles, mixin("a." ~ member))` though. I assume this should work because rules for alias this (as I understand) are to basically try if there's a member name that resolves the call, else forward to alias this. yeah, just built in properties/members don't pass the same existence checks so that's confusing your template constraint. I believe the same happens if it's not a built in property. You can replace B!int with B!S and have S contains a function "f" (for instance), then whether or the not constraint on opDispatch is: __traits(compiles, mixin("t." ~ name)) or hasMember(T, name) You still get: b.p.writeln; // Error: no property 'p' for type 'B!(S)'
Re: Converting array in to aliased tuple type.
On Monday, 25 December 2017 at 17:59:54 UTC, visitor wrote: On Monday, 25 December 2017 at 15:03:08 UTC, aliak wrote: On Monday, 25 December 2017 at 14:08:08 UTC, Mengu wrote: I was kind of hoping for some magical D variadic alias template on Tuple or something that will just deconstruct the arguments in to tuple components. i don't think it's better but : https://run.dlang.io/is/2rgOzh Heh, no it's probably not, but interesting! So it does work with a static array. Is it that .array on a range is not able to infer a size and hence produce a static array for the given situation? Is this a D limitation, a logical one or maybe just not implemented yet? Cheers!
Re: std way to remove multiple indices from an array at once
On Thursday, 21 December 2017 at 15:59:44 UTC, Steven Schveighoffer wrote: Here's a similar solution with an actual range: https://run.dlang.io/is/gR3CjF Note, all done lazily. However, the indices must be sorted/unique. -Steve Noice! :D
Re: std way to remove multiple indices from an array at once
On Thursday, 21 December 2017 at 00:52:29 UTC, Nicholas Wilson wrote: On Thursday, 21 December 2017 at 00:23:08 UTC, Steven Schveighoffer wrote: I'm assuming here indices is sorted? Because it appears you expect that in your code. However, I'm going to assume it isn't sorted at first. auto sortedIdxs = indices.assumeSorted; // also could be = It's not going to be as good as hand-written code, complexity wise, but it's definitely shorter to write :) -Steve If indices is sorted with no duplicates and random access then you can do it in linear time. Ah yes, I guess sorted and unique as well would be the expected input. But nice to see the handling of non-sorted indices. I tried to search for an assumeUnique function as well (the assumeSorted one was nice to see) but it's not what I thought it'd be - seems to be more of an assumeUnaliased. And I guess there's no assumeUniqueElements. And the filter approach is nice! :) (just need to account for the last ii++ (when filter comes back in I think one case would be ii == indices.length and you'd get a range error) Thanks for the feedback!
std way to remove multiple indices from an array at once
Hi, is there a way to remove a number of elements from an array by a range of indices in the standard library somewhere? I wrote one (code below), but I'm wondering if there's a better way? Also, can the below be made more efficient? auto without(T, R)(T[] array, R indices) if (isForwardRange!R && isIntegral!(ElementType!R) && !isInfinite!R) { T[] newArray; ElementType!R start = 0; foreach (i; indices) { newArray ~= array[start .. i]; start = i + 1; } newArray ~= array[start .. $]; return newArray; } // Usage long[] aa = [1, 2, 3, 4] aa = aa.without([1, 3]) Thanks!
Re: How do you safely deal with range.front?
On Monday, 1 January 2018 at 04:18:29 UTC, Ali Çehreli wrote: If you're fine with specifying the function as a template argument, the following works. (As seen with 's => s.foo()' below, you have to use a lambda for member functions anyway.) Ali Nice! Thanks :) And I think your usage for something named "ifFront" actually makes more sense than using it to return "saferef" functionality. I've basically implemented an optional type for now and the "iffront" implementation looks like this: import std.range: isInputRange; auto iffront(Range)(Range r) if (isInputRange!Range) { import std.range: ElementType, empty, front; import optional: no, some; return r.empty ? no!(ElementType!Range) : some(r.front); } unittest { import std.algorithm: filter; assert([false].filter!"a".iffront.empty); // because optional is a range } unittest { import std.algorithm: filter; import optional: some, none; struct A { int f() { return 7; } } assert([A()].filter!"false".iffront.f == none); assert([A()].filter!"true".iffront.f == some(7)); } And thanks everyone for the input. I'll play around with some of the ideas and see what comes of it.
Re: How do you safely deal with range.front?
On Monday, 1 January 2018 at 02:18:36 UTC, Jonathan M Davis wrote: Except that the reason for arrays throwing RangeErrors when you try and index them out-of-bounds is to avoid memory safety issues, which is not necessarily the case at all when you're talking about ranges. Having ranges in general be checking empty in front, popFront, back, etc. would add unnecessary overhead - especially when you consider that ranges often wrap other ranges. You'd be layering on check after check when the calling code is already supposed to be checking empty when necessary to make sure that the range isn't empty. You'd even be layering checks on top of the checks that arrays already do, since many ranges are ultimately wrapped around a dynamic array at their core. [...] Makes sense. Especially after pointing out that ranges are mostly arrays at the end. Thanks!
Re: static if and early exit from function doesn't seem to work?
On Sunday, 31 December 2017 at 13:47:32 UTC, Adam D. Ruppe wrote: On Sunday, 31 December 2017 at 13:32:03 UTC, aliak wrote: So it seems it tries to compile the statements below the check on V.length even though it's guaranteed to be true and there's a return statement inside the if. Yeah, static if includes or excludes code independently at compile time. So what you wrote there would be like, assuming the first to static ifs pass: auto concat(R, V...)(R range, V values) if (isInputRange!R) { import std.range: chain, ElementType; return range; return range.chain(values[0]).concat(values[1..$]); } The code is still there, even if it isn't reached due to an early return, and thus still must compile. Using else static if means it won't be generated. Ah ok, thanks! So it is intended behavior. I wonder if treating a return like a static else would be a good idea though. I at least can't see how it would break anything at this time.
Re: UCFS does not work for nested functions?
On Monday, 18 June 2018 at 19:26:47 UTC, Steven Schveighoffer wrote: On 6/18/18 2:58 PM, aliak wrote: [...] It's the same in the fact that your call is silently switched to a different call. However, in the current syntax, an external entity CANNOT override a local function. When you call the nested function, it's the nested function, no matter what else occurs outside (even in the local module). There is no precedent for local functions to be overridden by module-level functions. [...] Ah I see. So it's basically that locals take priority, but if you allow them to UFCS then that's not true anymore because members need to take priority. Ok yep, that makes sense. Thanks !
Can you tell if an template alias parameter is of a specific template?
Hi Is there a way to tell if an alias is to a template? I'm writing some algorithms and I need to distinguish between a binary predicate that provides "less than" and one that provides "equal to" semantics. So I have these two templates: template eq(alias pred) { alias eq = pred; } template lt(alias pred) { alias lt = pred; } Then in some place: static if (is(pred == eq)) { return eq(a, b); } else static if (is(pred == lt)) { return !lt(a, b) && !lt(b, a); // equality with less than predicate } else { // default assumptions about predicate } Then I can use it like: auto a = S!(eq!((a, b) => a == b)) ; auto a = S!(lt!((a, b) => a < b)); I've tried std.traits.TemplateOf and __traits(isSame and also variations of typeof and also traits.isInstanceOf. I wonder if I have to parameterize eq and lt over a compile time sequence instead? Any tips to get this working? Cheers, - Ali
Re: How to get an inout constructor working with a template wrapper
On Friday, 27 July 2018 at 14:38:27 UTC, Steven Schveighoffer wrote: On 7/27/18 9:29 AM, aliak wrote: Ok, thanks to Simen from another post [0], I just figured out what the correct constructor and factory method for a template wrapper should be: https://run.dlang.io/is/S4vHzL struct W(T) { T val; this(U : T, this This)(auto ref U val) { this.val = val; } } auto wrap(T)(auto ref T t) { return W!T(t); } Seems to catch all cases! And instantiate a new template for all mutabilities. Whereas inout would only instantiate one (and disallows modification of val if not const or immutable). -Steve If you change the ctor to be inout then you get (from the link above): onlineapp.d(4): Error: cannot implicitly convert expression val of type onlineapp.C to inout(C) onlineapp.d(28): Error: template instance `onlineapp.W!(C).W.__ctor!(C)` error instantiating onlineapp.d(4): Error: cannot implicitly convert expression val of type S1 to inout(S1) onlineapp.d(44): Error: template instance `onlineapp.W!(S1).W.__ctor!(S1)` error instantiating onlineapp.d(4): Error: cannot implicitly convert expression val of type onlineapp.C to inout(C) onlineapp.d(9): Error: template instance `onlineapp.W!(C).W.__ctor!(C)` error instantiating onlineapp.d(52):instantiated from here: wrap!(C) onlineapp.d(4): Error: cannot implicitly convert expression val of type const(C) to inout(const(C)) onlineapp.d(9): Error: template instance `onlineapp.W!(const(C)).W.__ctor!(const(C))` error instantiating onlineapp.d(53):instantiated from here: wrap!(const(C)) Am I applying inout incorrectly?
Re: How to avoid inout type constructor with Optional type wrapper undoing string type
On Friday, 27 July 2018 at 14:52:20 UTC, Steven Schveighoffer wrote: On 7/23/18 2:39 PM, aliak wrote: Hi, I'm playing around with an Optional wrapper type. It stores a type T and a bool that defines whether a value is defined or not: struct Optional(T) { T value; bool defined = false; this(U : T)(auto ref inout(U) value) inout { this.value = value; this.defined = true; } } Don't use inout here. The point of inout on the constructor is to *transfer* the mutability of the parameter to the struct instance. But you want to simply copy the type into the struct (an immutable(Optional!T) is quite useless, no?) Just use U, not inout(U), and don't put inout on the constructor. -Steve But then it only works for mutable Optional right? Why would an immutable(Optional!T) be useless? Data can be "forever" empty or a certain value.
Re: How to get an inout constructor working with a template wrapper
On Friday, 27 July 2018 at 14:34:54 UTC, Steven Schveighoffer wrote: The problem here is that inout(immutable(int)) is equivalent to immutable(int). That is, all flavors of mutability are equivalent to immutable(int): /*mutable*/(immutable(int)) => immutable(int) const(immutable(int)) => immutable(int) immutable(immutable(int)) => immutable(int) So the compiler really looks at your wrap instantiation like this; inout(W!(immutable(int))) wrap(immutable(int) t) Ah ok, so the compiler remove inout behind me back here? (And then tells me it needs to be there? :p) which triggers the (really bad) message. I'd ask, why are you even worrying about explicit instantiation? Why not just wrap(3)? Just because I don't see why it should not work really. Why not allow wrap!(immutable int)(3)? or (if you really want to test it) wrap(immutable(int)(3))? To make it compile successfully you can either: 1) Chance immutable to const, then it works for some reason. Because immutable(const(int)) => immutable(int), so the compiler can't remove the inout behind your back. 2) Change the line to: "auto si = wrap(cast(immutable int)3);" - i.e. do not explicitly provide type information. Yep, do this :) Note that the point of inout is 2-fold: 1. reduce template instantiations. In fact, wrap!int works for const, mutable and immutable int. 2. ENSURE that the data isn't modified, even in the case of mutable parameters. Thanks for the explanations! For some reason it's hard to get it all to *just work* right now without the template this. But it's probably some minor detail I'm just overlooking... -Steve
Re: Disabling opAssign in a type disabled all the opAssigns of an aliased type?
On Monday, 30 July 2018 at 18:47:06 UTC, Alex wrote: On Monday, 30 July 2018 at 18:30:16 UTC, aliak wrote: Is this a bug? If not is there a workaround? I would like for the alias this to function as a normal A type unless B specifically disables certain features, but it seems weird that disabling one opAssign disables all of them inside the aliases type but not in the aliasing type? struct A { void opAssign(int) {} } struct B { A a; alias a this; @disable void opAssign(float); } void main() { B b; b = 3; } Error: function `onlineapp.B.opAssign` is not callable because it is annotated with @disable Cheers, - Ali What happens if you omit the @disable line? Compiles ok then.
Re: Disabling opAssign in a type disabled all the opAssigns of an aliased type?
On Monday, 30 July 2018 at 20:20:15 UTC, Alex wrote: On Monday, 30 July 2018 at 19:33:45 UTC, aliak wrote: On Monday, 30 July 2018 at 18:47:06 UTC, Alex wrote: On Monday, 30 July 2018 at 18:30:16 UTC, aliak wrote: [...] What happens if you omit the @disable line? Compiles ok then. So... is this a valid workaround? ;) Hehe. Unfortunately not. It's for a proxy type that I need to disallow assignment to. But the proxy type uses alias to a T. So if T has a custom opAssign then bye bye functionality.
Disabling opAssign in a type disabled all the opAssigns of an aliased type?
Is this a bug? If not is there a workaround? I would like for the alias this to function as a normal A type unless B specifically disables certain features, but it seems weird that disabling one opAssign disables all of them inside the aliases type but not in the aliasing type? struct A { void opAssign(int) {} } struct B { A a; alias a this; @disable void opAssign(float); } void main() { B b; b = 3; } Error: function `onlineapp.B.opAssign` is not callable because it is annotated with @disable Cheers, - Ali
Re: Disabling opAssign in a type disabled all the opAssigns of an aliased type?
On Monday, 30 July 2018 at 20:38:33 UTC, aliak wrote: On Monday, 30 July 2018 at 20:20:15 UTC, Alex wrote: On Monday, 30 July 2018 at 19:33:45 UTC, aliak wrote: On Monday, 30 July 2018 at 18:47:06 UTC, Alex wrote: On Monday, 30 July 2018 at 18:30:16 UTC, aliak wrote: [...] What happens if you omit the @disable line? Compiles ok then. So... is this a valid workaround? ;) Hehe. Unfortunately not. It's for a proxy type that I need to disallow assignment to. But the proxy type uses alias to a T. So if T has a custom opAssign then bye bye functionality. The actual code is here if you're curious: https://github.com/aliak00/optional/pull/16/commits/93d51d790d313be3b108df2bd8b3699adc898bd0 Right now I've only: @disable this(); // Do not allow user creation of a Dispatcher @disable this(this) {} // Do not allow blitting either But I also want to @disable void opAssign(U)(Dispatcher!U) So that you can't reassign to the Dispatcher (i.e. proxy type) But if I do that then the opAssigns in the Optional!T cease functioning.
Re: How to avoid inout type constructor with Optional type wrapper undoing string type
On Sunday, 29 July 2018 at 12:30:58 UTC, Steven Schveighoffer wrote: On 7/28/18 6:06 PM, aliak wrote: [...] What I meant was that string is actually mutable (the data isn't mutable, but the string can be re-assigned to another one), so Optional!string is more useful than immutable(Optional!(char[])). I shouldn't have said that immutable(Optional!T) is useless, you are right, and it wouldn't make sense for the defined flag to change there anyway. [...] Ah right. So it seems inout is removing head qualifiers on by-val parameters? Filed what I think I understood from this: https://issues.dlang.org/show_bug.cgi?id=19125
Re: Disabling opAssign in a type disabled all the opAssigns of an aliased type?
On Monday, 30 July 2018 at 20:54:28 UTC, Simen Kjærås wrote: On Monday, 30 July 2018 at 18:30:16 UTC, aliak wrote: Is this a bug? If not is there a workaround? I would like for the alias this to function as a normal A type unless B specifically disables certain features, but it seems weird that disabling one opAssign disables all of them inside the aliases type but not in the aliasing type? struct A { void opAssign(int) {} } struct B { A a; alias a this; @disable void opAssign(float); } void main() { B b; b = 3; } Error: function `onlineapp.B.opAssign` is not callable because it is annotated with @disable The workaround is to not disable opAssign. :p Since this does work for other member functions that opAssign, I'm gonna say it's a bug - please file it in Bugzilla. A perhaps better workaround than the above is to wrap A's opAssigns. Sadly, this can't be done with template mixins, since they don't overload with non-mixins. It can be done with string mixins, however. It's also possible to encapsulate all this in a nice little template: struct A { void opAssign(int) {} void opAssign(float) {} } struct B { A a; alias a this; @disable void opAssign(float); mixin(wrap!(B, "opAssign")); } string wrap(T, string methodName)() { enum targetName = __traits(getAliasThis, T)[0]; return `import std.traits : Parameters, ReturnType; static foreach (e; __traits(getOverloads, typeof(`~targetName~`), "`~methodName~`")) static if (!is(typeof({static assert(__traits(isDisabled, getOverload!(typeof(this), "`~methodName~`", Parameters!e)));}))) ReturnType!e `~methodName~`(Parameters!e args) { return __traits(getMember, `~targetName~`, "`~methodName~`")(args); }`; } template getOverload(T, string name, Args...) { import std.traits : Parameters; import std.meta : AliasSeq; template impl(overloads...) { static if (overloads.length == 0) { alias impl = AliasSeq!(); } else static if (is(Parameters!(overloads[0]) == Args)) { alias impl = overloads[0]; } else { alias impl = impl!(overloads[1..$]); } } alias getOverload = impl!(__traits(getOverloads, T, name)); } unittest { B b; b = 3; static assert(!__traits(compiles, b = 3f)); } And that's enough magic for me for one night. -- Simen Heheh Amazing! In today's episode of extreme D (why is that not a thing?), we give you a "nice little template" :p https://issues.dlang.org/show_bug.cgi?id=19130 Would it take much to fix it up to use with templated opAssigns as well? I tried for a bit and got stuck with trying to get parameters and now I'm giving up for the time being. struct A { void opAssign(int) {} void opAssign()(float) {} } struct B(T) { A a; alias a this; @disable void opAssign(U)(B!U); import std.traits : Parameters, ReturnType; static foreach (t; __traits(getOverloads, A, "opAssign", true)) { static if (is(typeof(t.stringof))) { pragma(msg, t.stringof, " - ", Parameters!t); } else { pragma(msg, typeof(t), " - ", Parameters!t); } } } The Parameters!t of the template overloads all come out as "int" but only if there's the non-template opAssign(int) in A. If you remove that then you get errors. So something is fishy. Also I realized that it's just 2 opAssigns in the aliased Optional!T type for my specific use case so maybe, err... copy pasta them in. Cheers, - Ali