Will/should Option<T> implement IDisposable?
See original GitHub issueWhy 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:
- Created 6 years ago
- Comments:36 (34 by maintainers)
Top 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 >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
@StefanBertels That would be a bad idea.
Option
doesn’t own theT
, it is given to the constructor. And therefore it can’t be responsible for its lifetime.Also because(Ignore that second sentence, I have was having a brain freeze! The first sentence still stands though)Option
is a struct, it gets instantiated and finalised all the time, so it’s difficult to know whosOk, 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.
Changing
Option<>
to implementIDisposable
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.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 ofIDisoposable
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.
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 theIDisposable
interface.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 ofOption<>
. Conceptually, instead ofactor.Dispose()
also disposing yourIObservable<>
subscription, it would be more likeactor.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
and
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 callDispose
on theOption<>
instance can be delayed. The problem though is that this doesn’t obey the specification of theIDisposable
interface. The intent of this interface is that any instance of a class that implements this interface should haveDispose
called on that instance as soon as it is no longer needed, not when some object given in its constructor is no longer needed.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’sOption<>
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 thatOption<>
implementsIDisposable
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 aDisposedObjectException
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#'sint?
. Instead you have to call something likeMatch
orIfNone
, 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.
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.
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, constrainsT
to be astruct
and to implement some interface with methodFoo
, callsdefault(T)
to obtain an instance ofT
, and then callsFoo
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 theparseT
family of methods. Now of all theparseT
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.
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-disposableOption<>
and making it a disposable one.