Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix TestKitBase and Option<T> #4603

Merged
merged 4 commits into from
Nov 4, 2020

Conversation

Arkatufus
Copy link
Contributor

@Arkatufus Arkatufus commented Nov 3, 2020

Closes #4496.

Original problem was that

  • Option<T> failed to report that it has no value when passed a null value.
  • TestKitBase did not check for Option<Config>.None

@@ -30,7 +30,7 @@ public struct Option<T>
public Option(T value)
{
Value = value;
HasValue = true;
HasValue = value != null;
Copy link
Contributor

@IgorFedchenko IgorFedchenko Nov 3, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was always thinking about that - but what if null in a valid value for this optional thing? What if null means "there is a value, it is obtained from external source/whatever, it is initialized, and it is null right now"?
So I did not ever change that. Still not sure if we need to do that...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, by definition Option will return None or Some, what is the point of using Option if you still allow null to leak through? you wouldn't fix the original NRE problem.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even with this, you'll still have NRE problem if you forgot to check for None.

Copy link
Contributor Author

@Arkatufus Arkatufus Nov 3, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In essence, our Option<T> implementation is a half assed attempt to fix NRE problem, its just moving the problem somewhere else.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you still need a null, then what you need is a tri-state object, where its state can be null, none, and some

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it even broke our own code.
This is stupid, I feel like I'm chasing my own tail.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good fix actually, I would love to see exception thrown here too - but maybe this should be done on some breaking change release, i.e. Akka.NET 1.5, or even 2.0, because chances are that many users will need to debug their app and fix things.

Copy link
Contributor Author

@Arkatufus Arkatufus Nov 3, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the proper thing to do right now is to implement the right IOption<T> interface but make it internal and keep the old Option<T> struct for public consumption.
But this will require so so much work to convert every Option<T> in our code x__x

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we want to go full rage mode, switch to C# 8 and make the input type non-nullable :rage1: :rage2: :rage3: :rage4:

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need the IOption<T> interface exactly? I get the Some<T> and None<T>

Copy link
Member

@Aaronontheweb Aaronontheweb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some comments

@@ -1292,7 +1292,9 @@ namespace Akka.Streams.Dsl
public static Akka.Streams.Dsl.Flow<TIn, TOut2, TMat> Batch<TIn, TOut, TOut2, TMat>(this Akka.Streams.Dsl.Flow<TIn, TOut, TMat> flow, long max, System.Func<TOut, TOut2> seed, System.Func<TOut2, TOut, TOut2> aggregate) { }
public static Akka.Streams.Dsl.Flow<TIn, TOut2, TMat> BatchWeighted<TIn, TOut, TOut2, TMat>(this Akka.Streams.Dsl.Flow<TIn, TOut, TMat> flow, long max, System.Func<TOut, long> costFunction, System.Func<TOut, TOut2> seed, System.Func<TOut2, TOut, TOut2> aggregate) { }
public static Akka.Streams.Dsl.Flow<TIn, TOut, TMat> Buffer<TIn, TOut, TMat>(this Akka.Streams.Dsl.Flow<TIn, TOut, TMat> flow, int size, Akka.Streams.OverflowStrategy strategy) { }
[System.ObsoleteAttribute("Deprecated. Please use Collect(isDefined, collector) instead")]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@@ -30,7 +30,7 @@ public struct Option<T>
public Option(T value)
{
Value = value;
HasValue = true;
HasValue = value != null;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we want to go full rage mode, switch to C# 8 and make the input type non-nullable :rage1: :rage2: :rage3: :rage4:

@@ -30,7 +30,7 @@ public struct Option<T>
public Option(T value)
{
Value = value;
HasValue = true;
HasValue = value != null;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need the IOption<T> interface exactly? I get the Some<T> and None<T>

@Arkatufus
Copy link
Contributor Author

Arkatufus commented Nov 3, 2020

IOption<T> is implemented to simulate the scala implementation to the T, it would make Option unambiguous, a None is always a None and a Some will always contain something, it makes it save to do a typed switch on the type of the returned value.

We don't need it per se, but by implementing it we can separate the internal implementation of Option with the one that is being used by user, just in case changing Option will break someone elses code. Pretty much a bandaid with backward compatibility.

@Arkatufus
Copy link
Contributor Author

But it will be a very big bandaid, akin to using a nuclear warhead to kill an ant.

@Arkatufus
Copy link
Contributor Author

The big fear right now is that, even changing that line in Option might break someone elses code.
It already break ours, we're also (ab)using Option as a tri-state return value inside the Collect stream dsl.

@Aaronontheweb
Copy link
Member

As long as:

  1. It doesn't take long to implement;
  2. Doesn't introduce additional complexities / issues in terms of API compatibility; and
  3. Doesn't introduce lots of new allocations.

Then I think we should do it "the right way"

@Arkatufus
Copy link
Contributor Author

  1. Yes, it will take awhile to implement.
  2. We can change it as carefully as possible so that it doesn't change the public API
  3. It will probably have some memory impact.

@Arkatufus
Copy link
Contributor Author

Memory impact will most probably came from stack allocation, I think struct was chosen exactly because of this.

@Aaronontheweb
Copy link
Member

Memory impact will most probably came from stack allocation, I think struct was chosen exactly because of this.

that's fine then

@Aaronontheweb
Copy link
Member

as long as we don't allocate on every Option.[name of read method]

@Aaronontheweb Aaronontheweb merged commit ad8c0d1 into akkadotnet:dev Nov 4, 2020
@Arkatufus Arkatufus deleted the #4496_TestKitBase_bug branch March 8, 2021 13:29
@Arkatufus Arkatufus restored the #4496_TestKitBase_bug branch April 22, 2022 15:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Multiple tests run fails with Akka.TestKit 1.4.8 (worked fine with 1.4.7)
3 participants