question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Will/should Option<T> implement IDisposable?

See original GitHub issue

Why does Option<T> does not implement IDisposable? Can this be changed?

If I have a IDisposable type T and wrap it with Option<T> e.g. resources will not get freed automatically (in time). Think about Option<FileStream>.

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Comments:36 (34 by maintainers)

github_iconTop GitHub Comments

8reactions
louthycommented, Mar 24, 2017

@StefanBertels That would be a bad idea. Option doesn’t own the T, it is given to the constructor. And therefore it can’t be responsible for its lifetime. Also because Option is a struct, it gets instantiated and finalised all the time, so it’s difficult to know whos (Ignore that second sentence, I have was having a brain freeze! The first sentence still stands though)

2reactions
TysonMNcommented, Jan 2, 2018

The issue is about the request to have Option IDisposable. It was clear from beginning…

Ok, good. Thanks for clarifying. That is what I thought, but your previous comment made me think that you instead just wanted to add an additional data type with the desired behavior.

I feel that you want to just stop this request / do not respect positive outputs.

Changing Option<> to implement IDisposable is probably the smallest change (aka diff size) to fix the problem you had in your use of echo-process. That weighs in favor of making this change. But such small changes also directly correlate with being a hack.

This change wouldn’t affect just you and your code; it would affect everyone that uses Option<>, including me and our client’s projects. You believe that your actor project will benefit from this change, and I believe that our client’s projects would suffer for it. For example, Microsoft’s documentations warns that this would be a breaking change.

Warning It is a breaking change to add the IDisposable interface to an existing class. Because pre-existing consumers of your type cannot call Dispose, you cannot be certain that unmanaged resources held by your type will be released.

I can imagine a counter-argument being that this won’t break any pre-existing code because not calling Dispose in those cases is the correct thing to do. There are two issues with this. First, this shows that the proposed use of IDisoposable it incorrect because it would not match this specification. Second, it could break pre-existing code because what used to be a no-op (obj as IDisposable)?.Dispose() no longer is and the correctness could depend on that fact.

I do respect the positive outputs while also thinking that they do not out weight the negative outputs in this case. It is for that reason that I oppose this change.

there is no real concept (or at least a broken one) for IDisposable at all

Just because a window is broken doesn’t mean it is ok to break the one next to it. It is not wise to throw the baby out with the bath water. Of course the IDisposable interface is not ideal and the concept of ownership is unclear. Nevertheless, we still have a responsibility to our clients, our companies, and the .Net community at large to do the best we can with the IDisposable interface.

Lifetime management has to be done by developer and there are different methods to manage ownership.

There are multiple ways to do anything in life. Some ways are better, some ways are worse, and the usefulness of some ways depends on the situation. In general, the comparison of ways will only be a partial order, not a total one, so some ways are best in some situations but terrible in others. Just because the proposed change would benefit you doesn’t mean it would benefit everyone.

I have tried to explain my approach to lifetime management above. Here is some more insight into my understanding that I can share on that topic. It has only been within the last year that I have realized how useful it is, in all aspects of life, to delay decisions. In the past, my personality lead me to try and solve a problem as soon as it arised. I now have a better appreciation for the fact that by delaying a decision, I will often have more information about the problem, which allows me to make a better decision. This delay is not procrastination, which is waiting too long. Instead you should delay (as others have said) “until the last responsible moment”.

The connection is that I want to delay choices about lifetime until the last responsible moment. I think we can always responsibly delay the choice to dispose the instance given to the constructor of Option<> to somewhere outside of Option<>. Conceptually, instead of actor.Dispose() also disposing your IObservable<> subscription, it would be more like actor.Dispose(); actorState.Map(x => x.Dispose());.

Robert Martin discusses the benefit that delaying decisions has on your architecture. Here is a blog post that nicely summarizes what I have heard Robert say on the subject. And two exellent quotes from that post are

good architecture is less about the decisions you make and more about the decisions you defer making

and

The purpose of a good architecture is to defer decisions, delay decisions. The job of an architect is not to make decisions, the job of an architect is to build a structure that allows decisions to be delayed as long as possible.

I can imagine a counter-argument to this being that the proposed change allows for the delay of disposing the value inside the Option<> instance because the choice to call Dispose on the Option<> instance can be delayed. The problem though is that this doesn’t obey the specification of the IDisposable interface. The intent of this interface is that any instance of a class that implements this interface should have Dispose called on that instance as soon as it is no longer needed, not when some object given in its constructor is no longer needed.

If you give some example to show problems with disposable Option we could evaluate pros and cons.

Is the following specific enough?

Because of this, if the proposed change were accepted, I think the following will inevitably happen. Some developer will create a class that uses an unmanaged resource, and they will have their class implement IDisposable because they were told that this is the proper way to free an unmanaged resource. Locally speaking, this developer is correct to do so. Then they will wrap an instance of their class with language-ext’s Option<> because they were told that this is the proper way to model the possibility that their reference doesn’t exist. Locally speaking, this developer is correct to do so. Then they will notice that Option<> implements IDisposable and recall that they were told that each disposable instance should be disposed of as soon as possible. And, again, locally speaking, this developer is correct to do so. But now they will have a bug in their code when later they attempt to use their disposable instance and a DisposedObjectException is thrown.

My favorite part about strongly typed languages and functional programming is how it forces the developer to do the right thing. For example, if you want extract the value from Option<int>, you can’t just call .Value like you can with C#'s int?. Instead you have to call something like Match or IfNone, which forces the developer to consider the possibility that the value doesn’t exist.

My opposition to this change is an attempt to protect this developer. I want the data types in language-ext to force them to do the right thing. The problem with the proposed change is that it doesn’t force the developer to do the right thing. It does the opposite by giving them more ways in which to do the wrong thing.

But IMO some arguments are wrong (like mutability)

You probably know better than me. Is there any example of a monad with a non-pure method? I have only learned about and understand a few monads so far, and all of them only have pure methods.

Regarding dependency injection: This is some solution to a specific problem in OOP. But we can change design to avoid this. I don’t suggest to drop DI (maybe you should think about it 😉).

I think we have different understandings of DI.

I would say that Paul is using DI in language-ext when he passes a type parameter T into a method, constrains T to be a struct and to implement some interface with method Foo, calls default(T) to obtain an instance of T, and then calls Foo on that instance.

The implementation of this method “depends” on some method Foo and Paul “injects” that dependency into the method via a type parameter. Here of one example where Paul does this. One doesn’t have to get so fancy to do DI in FP though. A more traditional approach is to just pass in a delegate as a parameter, which is what I did in my recent refactor to the parseT family of methods. Now of all the parseT functions that I touched, only this one includes branching. The others only exist to provide an unambiguous function name for each type.

Maybe you equate DI with the use of a DI container (such Simple Injector)? I would agree with you then; it is my current understand that existing DI containers are rather specific to OOP. Even in OOP though, you don’t have to use a DI container to do the same injection of dependencies. Instead you can use pure DI.

Another place I like to go to learn more about functional programming the site F# for fun and profit by Scott Wlaschin. He has a nice blog post about what he thinks DI looks like in FP.

IMHO there is still a lot of value with disposable Option

Again, I am not primarily against having a disposable Option. I am primarily against not having a non-disposable Option<>; I am against changing the existing non-disposable Option<> and making it a disposable one.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Determining if IDisposable should extend an interface or ...
My options seem to be: 1) Implement IDisposable on the interface requiring all of the implementations to implement Dispose, even if only an ......
Read more >
CA1063: Implement IDisposable correctly (code analysis)
The System.IDisposable interface is not implemented correctly. Possible reasons for this include: IDisposable is reimplemented in the class.
Read more >
CA1001: Types that own disposable fields should be ...
Rule description. A class that declares an IDisposable field indirectly owns an unmanaged resource. The class should implement the IDisposable ...
Read more >
Implementing IDisposable
There are several options to consider when implementing the IDisposable interface. In this article we will start with the basic pattern that ...
Read more >
How to Implement IDisposable and Finalizers: 3 Easy Rules
This implementation of IDisposable should only call Dispose for each owned resource. It should not have any other code: no “if” statements, ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found