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
Moving to ValueTuples #3973
Moving to ValueTuples #3973
Conversation
Some I am using |
That makes sense. |
I'd work backwards - do separate PRs per-project, but do higher level projects that don't have any downstream dependencies (i.e. Cluster.Sharding, Akka.Persistence.Query, Streams) first, the move onto the heavy-duty projects like Akka.Remote, Akka, Akka.Cluster... |
All right, now this is going to be single huge PR that will replace |
@Aaronontheweb Have a question about Akka.Streams update. Here is an example, where all big change starts: public static FlowWithContext<TCtx, TIn, TCtx, TOut2, TMat> Collect<TCtx, TIn, TOut, TOut2, TMat>(
this FlowWithContext<TCtx, TIn, TCtx, TOut, TMat> flow, Func<TOut, TOut2> fn) where TOut2 : class
{
var stage = new Collect<(TOut, TCtx), (TOut2, TCtx)?>(func: x =>
{
var result = fn(x.Item1);
return ReferenceEquals(result, null) ? ((TOut2, TCtx)?)null : (result, x.Item2);
});
return flow.Via(Flow.FromGraph(stage));
} The public FlowWithContext<TCtxIn, TIn, TCtx2, TOut2, TMat> Via<TCtx2, TOut2, TMat2>(
IGraph<FlowShape<(TOut, TCtxOut), (TOut2, TCtx2)>, TMat2> viaFlow) =>
FlowWithContext.From(Flow.FromGraph(Inner).Via(viaFlow)); So it requires The solution of making this work without logical change is change While this can be done, I am getting lots of places where code like Additionally, there is lots of code like this: Source.From(Enumerable.Range(1, 5))
.MapMaterializedValue<(Task<IImmutableList<int>>, Task<IImmutableList<int>>, Task<IImmutableList<int>>)?>(_ => null); This is a part of test method, and Moving to nullable So, other suggestion is - should I change What do you think? |
@IgorFedchenko thanks for a detailed and thoughtful analysis. I'd also take a look at #3282 and see what @vasily-kirichenko did there when he worked on this problem a while back.
Nullable value tuples seem like hot garbage in this API, and generally speaking Akka.Streams shouldn't be returning I think the best approach here is to move away from nullable Can you make a list, briefly, of the areas where there will be some complex refactoring as a result of losing |
A second thought: null items inside a |
@Aaronontheweb About null items inside In general, I suspect that any time something is returning This idea is somewhat approved with PR you have referenced. In this PR, some classes that I have issues with are missing (like this - but I know that So I am going to use that way and keep going with non-nullable What do you think? |
About my last question: I have just tried to use |
cc @Horusiath - see @IgorFedchenko's post above about |
This function, that returns null if conversion result is null, is used on var result = WithSupervision(() => _stage._func(Grab(_stage.In)));
if (result.HasValue)
{
if (result.Value.IsDefaultForType())
Pull(_stage.In);
else
Push(_stage.Out, result.Value);
}
Seems like I got it, but please correct me if I am wrong. All in all, changing |
@IgorFedchenko that looks like a good solution. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM - but we're blocked until #3920 is resolved.
{ | ||
var entityId = messageExtractor.EntityId(msg); | ||
var entityMessage = messageExtractor.EntityMessage(msg); | ||
return Tuple.Create(entityId, entityMessage); | ||
return (entityId, entityMessage); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First real piece of non-test related ValueTuple
code I've seen (193 files) - this is SO MUCH nicer than what we've had historically with the traditional Tuple<,>
syntax. This is going to be great.
@@ -475,7 +475,7 @@ internal static class Shards | |||
case Shard.PassivateIdleTick _: | |||
shard.PassivateIdleEntities(); | |||
return true; | |||
case var _ when shard.ExtractEntityId(message) != null: | |||
case var _ when shard.ExtractEntityId(message).HasValue: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And yes, I love this too - no more null
checks. Or at least, fewer of them.
@@ -812,14 +812,14 @@ private DataEnvelope SetData(string key, DataEnvelope envelope) | |||
|
|||
private Digest GetDigest(string key) | |||
{ | |||
Tuple<DataEnvelope, Digest> value; | |||
(DataEnvelope, Digest) value; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A nitpick, and we definitely should not try to fit this into this pull request - we should name the ValueTuple
properties, especially in public APIs, so users don't have to use the .Item1
and .Item2
properties - this will also allow us to take advantage of C#7/8 language features like tuple deconstruction.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's plan on doing this in separate PRs as part of an ongoing "cleanup" effort, and those can be small case-by-case PRs.
@@ -4780,6 +4780,17 @@ namespace Akka.Util | |||
public static int StringHash(string s) { } | |||
public static int SymmetricHash<T>(System.Collections.Generic.IEnumerable<T> xs, uint seed) { } | |||
} | |||
public struct Option<T> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
{ | ||
public class static ObjectExtensions | ||
{ | ||
public static Akka.Util.Option<T> AsOption<T>(this T obj) { } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
public static bool IsDefaultForType<T>(this T obj) { } | ||
} | ||
public struct Option<T> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The other API changes in this file, all public changes to the Akka.Streams
APIs, are fine since they're mostly topical.
This change removes one class from the Akka.Streams.Util
namespace and moves it into Akka
- totally fine with that too. There's a good possibility one day that as C# adds more functional features to the language that it too will include a first-party Option
type. We will cross that bridge when we get there, but for the time being I'm happy having our own third-party solution to that problem.
{ | ||
var result = fn(x.Item1); | ||
return ReferenceEquals(result, null) ? null : Tuple.Create(result, x.Item2); | ||
return ReferenceEquals(result, null) ? default((TOut2, TCtx)) : (result, x.Item2); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is probably what we're going to have to do in cases where we can't use Option
or null
.
// Copyright (C) 2009-2019 Lightbend Inc. <http://www.lightbend.com> | ||
// Copyright (C) 2013-2019 .NET Foundation <https://github.com/akkadotnet/akka.net> | ||
// Copyright (C) 2015-2016 Lightbend Inc. <http://www.lightbend.com> | ||
// Copyright (C) 2013-2016 Akka.NET project <https://github.com/akkadotnet/akka.net> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copyright is off here for some reason
@@ -89,10 +89,10 @@ public TMat2 RunWith<TMat2>(IGraph<SourceShape<TIn>, TMat2> source, IMaterialize | |||
/// <param name="materializer">The materializer.</param> | |||
/// <returns>A tuple containing the (1) materialized value and (2) a new <see cref="Sink"/> | |||
/// that can be used to consume elements from the newly materialized <see cref="Sink"/>.</returns> | |||
public Tuple<TMat, Sink<TIn, NotUsed>> PreMaterialize(IMaterializer materializer) | |||
public (TMat, Sink<TIn, NotUsed>) PreMaterialize(IMaterializer materializer) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Areas like these on the public APIs are where I'd really appreciate having some names for the items inside the tuple, again for deconstruction purposes. I think we can add these in separate PRs.
|
||
if (tuple == null) | ||
if (!(message is ValueTuple<T, T> tuple)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Looks like my other PR didn't totally fix the issue on Linux - have some other build failures stemming from NuGet.exe failing to download... wonder if there's some azure connectivity problems, given that we had issues with pipelines on that other PR too. |
@Aaronontheweb In error report I still see errors like this:
But did not noticed anything related to connectivity issues, at least at that log... Should I take some time to try to resolve this? You fix looks good, and it is not clear where does it get reference to |
Hmm, can not reproduce this errors locally, when trying to build in inside |
My money is on the Mono compiler struggling with ValueTuple in some of the projects |
Don't have this issue with .NET Core compilation on Linux |
@Aaronontheweb Seems like you were right - disabling But we get another one, during
I could not quickly find where does this project get that reference - seems like it is inside one of the referenced packages... Anyway, this does not belong to public API, so I just rolled back |
@Aaronontheweb Build issues under linux are resolved now, but I had to make 2 more small fixes (rolling back to While this should not be a problem to the users, we will not be able to use |
I'd keep a separate PR for that - this will probably be fixed as a result of changing which version of .NET Framework / .NET Core / .NET Standard we ultimately support as part of Akka.NET v1.4.0. |
Akka.Persistence.TestKit.Tests.CounterActorTests.CounterActor_internal_state_will_be_lost_if_underlying_ Failure on all 3 platforms |
There may be race conditions because of creating new actor with same name as terminated actor from outside of actor's parent. Committed naming fix to avoid such conflict in this test. |
Couldn't get that last check to re-run, but it was a few racy specs that have a high flip rate. We should probably disable them. Nice work @IgorFedchenko . |
@Aaronontheweb After updating only few projects others won't compile because of changing signatures.
Should I continue creating separate PRs per project? In this case we could create some "value-tuples" branch in your repo and safely merge PRs until all projects will be updated and will compile, and then merge to dev with possible conflicts resolving.
Because we can not merge this PR to dev anyway until solution will compile, right?