www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.announce - Coming back, Unique, opDot and whatever else

reply "Stanislav Blinov" <stanislav.blinov gmail.com> writes:
Hello everyone! It's been a long time since I last posted here, 
I've been away from all things D, only being able to take an 
occasional peek from time to time. It's so good to be back. I'm 
now finding a bit of time to commit to learn D, more relearn as 
it would seem.

I've started my rediscoveries with exploring of concurrency, 
parallelism, threading in D, and after some time I found myself 
thinking "I need a unique encapsulator". Don't ask why, I may not 
even be able to answer it in a month. But that helped me solve 
some problems before in C++, so I thought why not try it here? In 
a couple of page views I came upon std.typecons and its Unique 
type. And I thought "why that is exactly what I want!". And it 
was, too. But after taking a closer look at its general 
implementation I just couldn't help myself but think "well, it 
seems it was done in a hurry, never finished, left as it was 
because this of that and whatnot". I mean, those sparse comments, 
things like "doesn't work yet", etc... I thought well, since I'm 
learning the language again, why not make it an exercise and fill 
those blanks? It'd certainly help me, because it would improve 
the abstraction I'm using, and because it's a learning experience.

So, here's what I came up with for now:

http://codepad.org/S4TfIdxc

Granted, not a complete implementation, keeping not very far from 
the original. But right now I think it's a good time to ask you 
guys what do you think? Where have I went wrong, what did I do 
incorrectly, what potential issues can you spot in this? I mean, 
I'm not asking about using opDot(), which, as I understand it, 
could be going away anytime now. At least I think I managed to 
fill in most of the "blanks" of the current implementation while 
keeping (almost?) to the same interface. In short, please destroy 
this with Big Fat Phazerz so I could take the remaining ashes and 
contemplate on the next iteration :)
Jan 16 2014
next sibling parent "Stanislav Blinov" <stanislav.blinov gmail.com> writes:
Oh woops, it seems I misclicked the links and ended up posing in 
.announce instead of .learn. Sorry about that! If this is in any 
way movable, I'd be obliged. Thanks!
Jan 16 2014
prev sibling next sibling parent reply "qznc" <qznc web.de> writes:
On Friday, 17 January 2014 at 01:12:04 UTC, Stanislav Blinov 
wrote:
 Hello everyone! It's been a long time since I last posted here, 
 I've been away from all things D, only being able to take an 
 occasional peek from time to time. It's so good to be back. I'm 
 now finding a bit of time to commit to learn D, more relearn as 
 it would seem.

 I've started my rediscoveries with exploring of concurrency, 
 parallelism, threading in D, and after some time I found myself 
 thinking "I need a unique encapsulator". Don't ask why, I may 
 not even be able to answer it in a month. But that helped me 
 solve some problems before in C++, so I thought why not try it 
 here? In a couple of page views I came upon std.typecons and 
 its Unique type. And I thought "why that is exactly what I 
 want!". And it was, too. But after taking a closer look at its 
 general implementation I just couldn't help myself but think 
 "well, it seems it was done in a hurry, never finished, left as 
 it was because this of that and whatnot". I mean, those sparse 
 comments, things like "doesn't work yet", etc... I thought 
 well, since I'm learning the language again, why not make it an 
 exercise and fill those blanks? It'd certainly help me, because 
 it would improve the abstraction I'm using, and because it's a 
 learning experience.

 So, here's what I came up with for now:

 http://codepad.org/S4TfIdxc

 Granted, not a complete implementation, keeping not very far 
 from the original. But right now I think it's a good time to 
 ask you guys what do you think? Where have I went wrong, what 
 did I do incorrectly, what potential issues can you spot in 
 this? I mean, I'm not asking about using opDot(), which, as I 
 understand it, could be going away anytime now. At least I 
 think I managed to fill in most of the "blanks" of the current 
 implementation while keeping (almost?) to the same interface.
Improving Phobos code by filling in the blanks is usually a good idea and a good learning experience as well. Changing an interface in Phobos is a big deal and should be thoroughly justified. Does it break backwards compatibility? Why is it necessary? (btw moving to .learn is not possible, unfortunately)
Jan 16 2014
parent "Stanislav Blinov" <stanislav.blinov gmail.com> writes:
On Friday, 17 January 2014 at 06:25:53 UTC, qznc wrote:

 Improving Phobos code by filling in the blanks is usually a 
 good idea and a good learning experience as well.
Oh, I wasn't proposing to stick that into Phobos, not in its current state. I'm not that arrogant :) Even given that I resolve all the TODOs that are currently there, it'd need a lot of discussion beforehand to even be proposed.
 Changing an interface in Phobos is a big deal and should be 
 thoroughly justified. Does it break backwards compatibility? 
 Why is it necessary?
Yes, it does. It disallows things like Unique!Car shiny = new Lamborghini(), which are possible in std Unique and are analagous to Unique!Car dubious = createCarAndSneakilySaveLotsOfReferencesToIt(). Initialization is completely taken over by createUnique(), which is basically a non-member implementation of this(Args...)(Args args) (commented out in typecons). Of course, Unique can not prevent the type itself to advertise aliases all over the application, but that's something that can't be enforced anyway. Also, its type support is currently very limited (std Unique can have just about anything that can be new'ed), but that's temporary, I'll be extending that. Other than these, I can't think of any other friction with current implementation. Though I'd have to write tests to be sure.
 (btw moving to .learn is not possible, unfortunately)
:(
Jan 17 2014
prev sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2014-01-17 02:12, Stanislav Blinov wrote:
 Hello everyone! It's been a long time since I last posted here, I've
 been away from all things D, only being able to take an occasional peek
 from time to time. It's so good to be back. I'm now finding a bit of
 time to commit to learn D, more relearn as it would seem.

 I've started my rediscoveries with exploring of concurrency,
 parallelism, threading in D, and after some time I found myself thinking
 "I need a unique encapsulator". Don't ask why, I may not even be able to
 answer it in a month. But that helped me solve some problems before in
 C++, so I thought why not try it here? In a couple of page views I came
 upon std.typecons and its Unique type. And I thought "why that is
 exactly what I want!". And it was, too. But after taking a closer look
 at its general implementation I just couldn't help myself but think
 "well, it seems it was done in a hurry, never finished, left as it was
 because this of that and whatnot". I mean, those sparse comments, things
 like "doesn't work yet", etc... I thought well, since I'm learning the
 language again, why not make it an exercise and fill those blanks? It'd
 certainly help me, because it would improve the abstraction I'm using,
 and because it's a learning experience.

 So, here's what I came up with for now:

 http://codepad.org/S4TfIdxc

 Granted, not a complete implementation, keeping not very far from the
 original. But right now I think it's a good time to ask you guys what do
 you think? Where have I went wrong, what did I do incorrectly, what
 potential issues can you spot in this? I mean, I'm not asking about
 using opDot(), which, as I understand it, could be going away anytime
 now. At least I think I managed to fill in most of the "blanks" of the
 current implementation while keeping (almost?) to the same interface. In
 short, please destroy this with Big Fat Phazerz so I could take the
 remaining ashes and contemplate on the next iteration :)
opDot has been replaced with opDispatch [1] and "alias this" [2]. Why can't safe be used, can you use trusted instead? You should probably use template constraints for "createUnique" as well. As for coding style, especially if you're aiming for including in Phobos: * Function names never start with underscore and always starts with a lowercase letter * I would prefer the same for instance variables as well, but I know there are several cases in Phobos where instance variables starts with an underscore [1] http://dlang.org/operatoroverloading.html#Dispatch [2] http://dlang.org/class.html#AliasThis -- /Jacob Carlborg
Jan 16 2014
parent reply "Stanislav Blinov" <stanislav.blinov gmail.com> writes:
On Friday, 17 January 2014 at 07:34:58 UTC, Jacob Carlborg wrote:

 opDot has been replaced with opDispatch [1] and "alias this" 
 [2].
I know, it was just a shortcut "for now". Although alias this is of little help in case of Unique, at least without additional "middle man". After all, the whole purpose of Unique is to swallow the reference and never let anyone outside the family lay hands on it :) Surely, opDot() is not a panacea either, you can always steal a reference by doing auto x = u.opDot(). I was thinking about brewing up a middle man using reflection. A hypothetical PublicInterface(T) mixin which could dispatch function calls with opDispatch and public data access with its own properties. But that's limiting. I haven't completely thought it over yet, and am open for suggestions :)
 Why can't  safe be used, can you use  trusted instead?
As far as I understand it, it can. I mean, I don't see where I would violate any of the rules attached to safe. The only danger zone is dereference, and that's protected by throwing. But I commented it out because I started having doubts: in my unittests (they're in separate module so they have the same access as the user code would) I have three simple classes that don't have any safe attributes on their functions. Unique would fail to compile with those ("failed semantic analysis"). So it would seem it's rather restrictive. I don't mind imposing correctness, but how far should it go?
 You should probably use template constraints for "createUnique" 
 as well.
In my opinion, static assert allows for nicer error messages.
 As for coding style, especially if you're aiming for including 
 in Phobos:
If only in some distant future :)
 * Function names never start with underscore and always starts 
 with a lowercase letter
Oh, yeah, those. Those would go away outside, as private members of a module :) But that brings another point. No matter how opDot() could be replaced (be it alias this or that potential mixin I mentioned), Unique does have a public method of its own (release()). How would one resolve collisions in this case? I mean, if a wrapped object has its own release() method? In the opDot() case, one can write x.opDot().release(). Could similar be done with alias this?
 * I would prefer the same for instance variables as well, but I 
 know there are several cases in Phobos where instance variables 
 starts with an underscore
Got it, thanks!
Jan 17 2014
parent reply Jacob Carlborg <doob me.com> writes:
On 2014-01-17 16:18, Stanislav Blinov wrote:

 As far as I understand it, it can. I mean, I don't see where I would
 violate any of the rules attached to  safe. The only danger zone is
 dereference, and that's protected by throwing. But I commented it out
 because I started having doubts: in my unittests (they're in separate
 module so they have the same access as the user code would) I have three
 simple classes that don't have any  safe attributes on their functions.
 Unique would fail to compile with those ("failed semantic analysis"). So
 it would seem it's rather restrictive. I don't mind imposing
 correctness, but how far should it go?
Just try and safe and see what happens. These really are the cases where you can rely on the compiler telling you when you're do something wrong.
 In my opinion, static assert allows for nicer error messages.
Yes, I agree. We had a discussion about this recently. I don't recall now, but there are some other advantages of using template constrains. Like it's working properly with __traits(compile).
 Oh, yeah, those. Those would go away outside, as private members of a
 module :)
Doesn't matter, private could should look just as good as public ;)
 But that brings another point. No matter how opDot() could be replaced
 (be it alias this or that potential mixin I mentioned), Unique does have
 a public method of its own (release()). How would one resolve collisions
 in this case? I mean, if a wrapped object has its own release() method?
 In the opDot() case, one can write x.opDot().release(). Could similar be
 done with alias this?
I don't think so, but via opDispatch you can do: x.opDispatch!("release")(); -- /Jacob Carlborg
Jan 17 2014
parent reply "Stanislav Blinov" <stanislav.blinov gmail.com> writes:
On Friday, 17 January 2014 at 20:06:08 UTC, Jacob Carlborg wrote:

 Unique would fail to compile with those ("failed semantic 
 analysis"). So
 it would seem it's rather restrictive. I don't mind imposing
 correctness, but how far should it go?
Just try and safe and see what happens. These really are the cases where you can rely on the compiler telling you when you're do something wrong.
Ahem. Wasn't it what I said? :) Of course I did try. My concern is that it would stop accepting arbitrary user types. Most times this would be a good thing. But not always. Hence I'm hesitating :)
 In my opinion, static assert allows for nicer error messages.
Yes, I agree. We had a discussion about this recently. I don't recall now, but there are some other advantages of using template constrains. Like it's working properly with __traits(compile).
Hmm... That's an interesting notice, I'd need to investigate. Thank you.
 Oh, yeah, those. Those would go away outside, as private 
 members of a
 module :)
Doesn't matter, private could should look just as good as public ;)
Oh, I didn't say so, but of course once they're out of class, they're bound to have proper names. I don't know if you've caught that comment in the code, but I basically intentionally named them ugly to minimize collisions.
 In the opDot() case, one can write x.opDot().release(). Could 
 similar be
 done with alias this?
I don't think so, but via opDispatch you can do: x.opDispatch!("release")();
Heh, now I'm even more leaning towards a proxy. Because honestly, x.opDispatch!("release")() is even uglier than x.opDot().release() :D By proxy I mean replacing opDot() with a mixin that would directly inject a Unique with an interface of a wrapped object. That has some problems (like how to forward calls to template functions), but hey, I'm experimenting here! It also occured to me that with alias this you could do something like (cast(X)x).release()... which may or may not do what one expects, and that (in case of both opDot() and alias this) would depend on what exactly do those return (a copy, a reference, etc.).
Jan 17 2014
next sibling parent "Stanislav Blinov" <stanislav.blinov gmail.com> writes:
On Friday, 17 January 2014 at 20:25:37 UTC, Stanislav Blinov 
wrote:

 Heh, now I'm even more leaning towards a proxy. Because 
 honestly, x.opDispatch!("release")() is even uglier than 
 x.opDot().release() :D...
Well, what can I say? I didn't need to go very far. Because Phobos has such proxy. Which is called std.typecons.Proxy :) I skimmed over it in the docs several times, probably because the docs just say "Make proxy for a." without going into much detail. But the last time I spotted it, associative memory kicked in, I guess, and I stayed a bit longer to actually look at the example. There is, however, at least one issue with it: template arguments are not forwarded properly: --- class Widget { auto inconsistent(T...)(T args) if (T.length) { return args[0]; } } auto widget = new Widget; widget.inconsistent("hello", 10, 20); // works auto uwidget = createUnique!Widget(); uwidget.inconsistent("hello", 10, 20); // Error: template instance inconsistent!() does not match template declaration inconsistent(T...)(T args) if (T.length) uwidget.inconsistent!(string,int,int)("hello", 10, 20); //works --- I hadn't had a chance for a closer inspection yet as to why though. In any case, that Proxy is a nice thing to have in the library!
Jan 17 2014
prev sibling next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2014-01-17 21:25, Stanislav Blinov wrote:

 Ahem. Wasn't it what I said? :)
I don't know, perhaps I missed that :)
 Of course I did try. My concern is that
 it would stop accepting arbitrary user types. Most times this would be a
 good thing. But not always. Hence I'm hesitating :)
Right, but the opposite could be said as well. If it's not marked with safe no other functions marked with safe can call the function. But any non-safe function can call safe functions.
 Oh, I didn't say so, but of course once they're out of class, they're
 bound to have proper names. I don't know if you've caught that comment
 in the code, but I basically intentionally named them ugly to minimize
 collisions.
I didn't think of that. Perhaps using two underscores instead then, since the compiler is has reserved those names no one else should use them. I think this is an edge case where this could be allowed. Alternatively, as you wrote in a comment, use free functions. Since they would be declared in the same module they would have access to the private data. Hmm, but the compiler prefers opDispatch before UFCS.
 Heh, now I'm even more leaning towards a proxy. Because honestly,
 x.opDispatch!("release")() is even uglier than x.opDot().release() :D By
 proxy I mean replacing opDot() with a mixin that would directly inject a
 Unique with an interface of a wrapped object. That has some problems
 (like how to forward calls to template functions), but hey, I'm
 experimenting here!
Does opDot even work? -- /Jacob Carlborg
Jan 18 2014
parent reply "Stanislav Blinov" <stanislav.blinov gmail.com> writes:
On Saturday, 18 January 2014 at 11:00:44 UTC, Jacob Carlborg 
wrote:
 Ahem. Wasn't it what I said? :)
I don't know, perhaps I missed that :)
Perhaps :o)
 Of course I did try. My concern is that
 it would stop accepting arbitrary user types. Most times this 
 would be a
 good thing. But not always. Hence I'm hesitating :)
Right, but the opposite could be said as well. If it's not marked with safe no other functions marked with safe can call the function. But any non-safe function can call safe functions.
I think I found out where I was going wrong with safe. I was misinterpreting error messages. After rereading the docs and some older posts on these newsgroups it all clicked into place. In my current iteration all functions are safe. Makes sense, since all they do is moving/nullifying one poor pointer around :) Exceptions are clearUnique(), because it calls destroy() (which is system), and createUnique() itself (so that it could call constructors). Both are trusted. But it would seem all of this would be irrelevant soon, since there's ongoing work on allowing automagic inference of attributes for methods of template classes/structs.
 Alternatively, as you wrote in a comment, use free functions. 
 Since they would be declared in the same module they would have 
 access to the private data. Hmm, but the compiler prefers 
 opDispatch before UFCS.
That's the approach I took. I've moved both _Transfer and _Clear into module scope and renamed them. And indeed, the compiler prefers opDispatch(). But not if I call them like .clearUnique(this) :) So the only public method collision that's possible right now is release(), but there's nothing that can be done about it. If I move it outside too, compiler would want to call wrong method is encapsulated type declares its own release().
 Does opDot even work?
Yes, but I ditched it too already :) I've also added some more operations, like casts for class types, null assignment, and an operator to squeeze the value from the Unique back into normal type land (discarding "uniqueness", of course). But I feel kind of uncomfortable having it like an operator, even though this looks sort of sensible: Widget a; Unique!Widget u; //... a << u; // after that a holds the value and u is null Maybe this would be better off as another method. What do you think? That's how it all looks currently: http://codepad.org/21vejNKK
Jan 18 2014
parent Jacob Carlborg <doob me.com> writes:
On 2014-01-18 13:07, Stanislav Blinov wrote:

 That's the approach I took. I've moved both _Transfer and _Clear into
 module scope and renamed them. And indeed, the compiler prefers
 opDispatch(). But not if I call them like .clearUnique(this) :)
Right, I was mostly thinking of public methods, but those are private.
 So the only public method collision that's possible right now is
 release(), but there's nothing that can be done about it. If I move it
 outside too, compiler would want to call wrong method is encapsulated
 type declares its own release().

 Does opDot even work?
Yes, but I ditched it too already :) I've also added some more operations, like casts for class types, null assignment, and an operator to squeeze the value from the Unique back into normal type land (discarding "uniqueness", of course). But I feel kind of uncomfortable having it like an operator, even though this looks sort of sensible: Widget a; Unique!Widget u; //... a << u; // after that a holds the value and u is null Maybe this would be better off as another method. What do you think?
Absolutely. We don't want operator overload abuse.
 That's how it all looks currently: http://codepad.org/21vejNKK
Now with tests :) -- /Jacob Carlborg
Jan 18 2014
prev sibling parent reply Rory McGuire <rjmcguire gmail.com> writes:
opDispatch works do you can just type object.release
Jan 18 2014
parent "Stanislav Blinov" <stanislav.blinov gmail.com> writes:
On Sunday, 19 January 2014 at 22:25:08 UTC, Rory McGuire wrote:
 opDispatch works do you can just type object.release
This will call Unique's release(). To get to wrapped object's release(), you need object.opDispatch("release")().
Jan 19 2014