October 07, 2015
On 10/6/2015 7:04 PM, bitwise wrote:
> On Wednesday, 7 October 2015 at 01:27:27 UTC, Walter Bright wrote:
>> On 10/4/2015 11:02 AM, bitwise wrote:
>>> For example, streams.
>>
>> No streams. InputRanges.
>
> This is too vague to really respond to. It does serve as an example of the
> over-emphasis on ranges in the D community. Ranges are great, but not for
> everything.

What can a stream do that a range cannot?

October 07, 2015
On Wednesday, 7 October 2015 at 02:41:12 UTC, Walter Bright wrote:
> On 10/6/2015 7:04 PM, bitwise wrote:
>> On Wednesday, 7 October 2015 at 01:27:27 UTC, Walter Bright wrote:
>>> On 10/4/2015 11:02 AM, bitwise wrote:
>>>> For example, streams.
>>>
>>> No streams. InputRanges.
>>
>> This is too vague to really respond to. It does serve as an example of the
>> over-emphasis on ranges in the D community. Ranges are great, but not for
>> everything.
>
> What can a stream do that a range cannot?

I was trying to make my case for polymorphism, so I haven't thought much about streams specifically, but one obvious thing that stands out is growing on demand.

Stream s = new Stream(4);
s.write(1);
s.write(2); // underlaying buffer grows automatically

I don't see how you would do something like this with a range.

When it comes to an InputRange, I suppose you're right. A BinaryReader could be generalized to read any range.

Again though, if I have to restate what I've been arguing for as simply as possible, it's that I want to use RAII and polymorphism at the same time, as a natural language solution. DIP74 would satisfy everything I'm asking for.

I've detailed my reasoning in this thread already, but structs alone, and structs wrapping classes are not goods solutions.

    Bit


October 07, 2015
On Wednesday, 7 October 2015 at 00:17:37 UTC, bitwise wrote:
> -again, alias this allows class references to escape their RAII containers and can cause access violations as show here:
> http://forum.dlang.org/post/zfggjsjmfttbcekqwgjd@forum.dlang.org

This isn't really a problem as it can be easily fixed. It's just that the original writer of Scoped made the mistake of allowing implicit conversion of a Scoped!C to a C, which when you think about it doesn't make any sense and is actually quite dangerous (as my post that you cited shows). All that has to be done to fix that is to disallow implicit conversion to the wrapped type while retaining the interface, which we can do today with a myriad of different methods (mixins, opDispatch, std.typecons.Proxy, etc.). It's just that nobody has done it.
October 07, 2015
On 10/6/2015 7:57 PM, bitwise wrote:
>> What can a stream do that a range cannot?
>
> I was trying to make my case for polymorphism, so I haven't thought much about
> streams specifically, but one obvious thing that stands out is growing on demand.
>
> Stream s = new Stream(4);
> s.write(1);
> s.write(2); // underlaying buffer grows automatically
>
> I don't see how you would do something like this with a range.

It's what an OutputRange does.


> Again though, if I have to restate what I've been arguing for as simply as
> possible, it's that I want to use RAII and polymorphism at the same time, as a
> natural language solution. DIP74 would satisfy everything I'm asking for.

Yes. We need to do that.

October 07, 2015
On 06-Oct-2015 23:44, ponce wrote:
> On Tuesday, 6 October 2015 at 17:20:39 UTC, Jonathan M Davis wrote:
>
> Unfortunately, it is quite common to need both virtual functions and
> deterministic destruction. It isn't helpful to disregard the problem by
> saying "you should have used a struct", in many cases it's not any easier.
>

+111

-- 
Dmitry Olshansky
October 07, 2015
On Tuesday, 6 October 2015 at 20:43:42 UTC, bitwise wrote:
> On Tuesday, 6 October 2015 at 18:43:42 UTC, Jonathan M Davis wrote:
>> On Tuesday, 6 October 2015 at 18:10:42 UTC, bitwise wrote:
>>> On Tuesday, 6 October 2015 at 17:20:39 UTC, Jonathan M Davis wrote:
>>> I'm not sure what else I can say. The example I posted says it all, and it can't be done properly in D (or C#, but why lower the bar because of their mistakes? ;)
>>
>> It's a side effect of having the lifetime of an object managed by the GC. There's no way around that except to use something else like manual memory management or reference counting.
>
> You are literally repeating what I just said in different words.
>
>> in D, it's a good reason to use structs to manage resources like that, and since most objects really have no need of inheritance and have no business being classes, it's usually fine.
>
> This is an opinion.
>
> I want polymorphism AND deterministic destruction, and the least you could do is just admit that it's a downside to D not having it, instead of trying to tell me that everything I know is wrong..
>
>> But in the cases where you do have to use a class, it can get annoying.
>
> YES, its does, and it's not just an odd case here and there..
>
>> You simply do not rely on the GC or the destruction of the object to free system resources. You manually call a function on the object to free those resources when you're done with it.
>
> I'm sorry, but I almost can't believe you're saying this.
>
> So, you're saying you want me to just revert back to manual resource management and accept that huge resources like textures and such may just leak if someone doesn't use them right? or throws an exception? in a language like D that is supposed to be safe?
>
>> In the case of C#, they have a construct to help with it that (IIRC) is something like
>>
>> using(myObj)
>> {
>> } // myObj.dispose() is called when exiting this scope
>
> For the THIRD time, I'll post my example:
>
> class Texture { }
> class Texture2D : Texture {
>     this() { /* load texture... */ }
>     ~this { /* free texture */ }     // OOPS, when, if ever, will this be called?
> }
>
> Now, does this really seem like a realistic use case to you?
>
> using(Texture tex = new Texture2D) {
>     // ...
> }
>

That no, but this yes (at least in C#):

using (LevelManager mgr = new LevelManager())
{
     //....
     // Somewhere in the call stack
     Texture text = mgr.getTexture();
}
--> All level resources gone that require manual management gone
--> Ask the GC to collect the remaining memory right now

If not level wide, than maybe scene/section wide.

However I do get that not all architectures are amendable to be re-written in a GC friendly way.

But the approach is similar to RAII in C++, reduce new to minimum and allocate via factory functions that work together with handle manager classes.

--
Paulo


October 07, 2015
On Wednesday, 7 October 2015 at 07:24:03 UTC, Paulo Pinto wrote:
>
> That no, but this yes (at least in C#):
>
> using (LevelManager mgr = new LevelManager())
> {
>      //....
>      // Somewhere in the call stack
>      Texture text = mgr.getTexture();
> }
> --> All level resources gone that require manual management gone
> --> Ask the GC to collect the remaining memory right now
>
> If not level wide, than maybe scene/section wide.
>
> However I do get that not all architectures are amendable to be re-written in a GC friendly way.
>
> But the approach is similar to RAII in C++, reduce new to minimum and allocate via factory functions that work together with handle manager classes.
>
> --
> Paulo

This is similar to Scoped!T in D. But this is not composable either.
You cannot have a "using()" field in a class object, much like you cannot have a Scoped!T field in D. In C#, you still have to implement IDispose interface AFAIK.


October 07, 2015
On Wednesday, 7 October 2015 at 07:35:05 UTC, ponce wrote:
> On Wednesday, 7 October 2015 at 07:24:03 UTC, Paulo Pinto wrote:
>>
>> That no, but this yes (at least in C#):
>>
>> using (LevelManager mgr = new LevelManager())
>> {
>>      //....
>>      // Somewhere in the call stack
>>      Texture text = mgr.getTexture();
>> }
>> --> All level resources gone that require manual management gone
>> --> Ask the GC to collect the remaining memory right now
>>
>> If not level wide, than maybe scene/section wide.
>>
>> However I do get that not all architectures are amendable to be re-written in a GC friendly way.
>>
>> But the approach is similar to RAII in C++, reduce new to minimum and allocate via factory functions that work together with handle manager classes.
>>
>> --
>> Paulo
>
> This is similar to Scoped!T in D. But this is not composable either.
> You cannot have a "using()" field in a class object, much like you cannot have a Scoped!T field in D. In C#, you still have to implement IDispose interface AFAIK.

If you reduce everything to just using(), yes you are right.

However, with a bit of functional programming flavor you don't really need to implement  IDispose.

Just have a wrapper function own the resource.

withLevelManager (mgr => {

     //..
     Texture text = mgr.getTexture();
});

And when one is able to use languages that offer syntax sugar for closures as last parameter, it can be improved to

withLevelManager {

     //..
     Texture text = it.getTexture();
};


No need to implement any interface, just like a RAII handler implementation in C++.

Of course, this assumes all resources that were allocated via the level manager are going to die after the scope ends. If any reference to any of them is kept somewhere else, then something bad will happen when it gets eventually used again.

Unless I am missing something, at least in the GC languages I am used to, there isn't a problem with the member fields as long all resources that require deterministic release follow a similar pattern. Like with the _ptr<>() classes in C++, new should only exist in the deepest layers for such classes.

I guess a problem with D is the bugs that interactions between classes and structs still have.

--
Paulo
October 07, 2015
On Wednesday, 7 October 2015 at 02:41:12 UTC, Walter Bright wrote:
> On 10/6/2015 7:04 PM, bitwise wrote:
>> On Wednesday, 7 October 2015 at 01:27:27 UTC, Walter Bright wrote:
>>> On 10/4/2015 11:02 AM, bitwise wrote:
>>>> For example, streams.
>>>
>>> No streams. InputRanges.
>>
>> This is too vague to really respond to. It does serve as an example of the
>> over-emphasis on ranges in the D community. Ranges are great, but not for
>> everything.
>
> What can a stream do that a range cannot?

I wouldn't know about streams vs ranges, but I have worked with Eric Meijer's RX and observables, and it has helped a lot with asynchronous/evented code. Mostly because of the FP thrown in. In a way it is very similar to ranges and std.algorithm.

A range, however, is inherently synchronous and blocking.

If streams could be asynchronous, wouldn't that qualify?
October 07, 2015
On Wednesday, 7 October 2015 at 07:35:05 UTC, ponce wrote:
> On Wednesday, 7 October 2015 at 07:24:03 UTC, Paulo Pinto wrote:
>>
>> That no, but this yes (at least in C#):
>>
>> using (LevelManager mgr = new LevelManager())
>> {
>>      //....
>>      // Somewhere in the call stack
>>      Texture text = mgr.getTexture();
>> }
>> --> All level resources gone that require manual management gone
>> --> Ask the GC to collect the remaining memory right now
>>
>> If not level wide, than maybe scene/section wide.
>>
>> However I do get that not all architectures are amendable to be re-written in a GC friendly way.
>>
>> But the approach is similar to RAII in C++, reduce new to minimum and allocate via factory functions that work together with handle manager classes.
>>
>> --
>> Paulo
>
> This is similar to Scoped!T in D. But this is not composable either.
> You cannot have a "using()" field in a class object, much like you cannot have a Scoped!T field in D. In C#, you still have to implement IDispose interface AFAIK.

You must implement IDisposable.

using(obj = new Obj()) {
  obj.DoSomething();
}

is syntactic sugar transformed by the C# compiler in:

obj = new Obj();
try {
  obj.DoSomething();
}
finally {
  if (obj != null) (IDisposable)obj.Dispose();
}

This can be easily translated to D using a delegate instead of the using block, if someone is keen to use this pattern. More than that, due to TLS, there is no need to worry about thread safety in this case.

Resources are discarded in Dispose(), the class destructor is just calling Dispose().