-
Notifications
You must be signed in to change notification settings - Fork 1k
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
Proposal: update blocks #194
Comments
Immutability aside (and examples are throwing away the intermediate results); these are the type of patterns I'd like to use with ref returns combined with "ref extension methods on structs" #186 |
If we think about it, we have something like that for operators
so how about
Or perhaps the proposed pipe forward operators could make this work (#96), eg.
|
What's wrong with huge expression statements? var x = CreateNewImmutableObject()
.SomeOperation1()
.SomeOperation2()
.SomeOperation3()
.SomeOperation4(); They only become unwieldy when you have to modify nested immutables (#162), and your proposal doesn't cover that use case. |
I do not find any of your examples to be compelling reasons for such new syntax. In all cases, there are simpler - existing - ways of expressing the same functionality: Example 1 could be written as: return CreateImmutableObject().SomeOperation().SomeOperation(); Example 2, as: ImmutableList<T> Create<T>(T first, T second, T third) =>
ImmutableList.Create(first, second, third); And example 3 as: int MinOfFour(int number0, int number1, int number2, int number3) =>
Min(Min(Min(number0, number1), number2), number3); This last example could be made even simpler using one of the proposed syntaxes for forward pipes: int MinOfFour(int number0, int number1, int number2, int number3) =>
Min(number0, number1) |> Min(@, number2) |> Min(@, number3); |
I think it's common to interleave updating immutable objects with other operations. For example, the non-buggy version of the var hash = HashCodeCombiner.Start()
.Add(_tagName, StringComparer.Ordinal)
.Add(_innerContent, StringComparer.Ordinal);
foreach (var kvp in _attributes)
{
hash = hash.Add(kvp.Key, StringComparer.Ordinal).Add(kvp.Value, StringComparer.Ordinal);
}
return hash.Build(); How would that look with update var hash in
{
HashCodeCombiner.Start();
hash.Add(_tagName, StringComparer.Ordinal);
hash.Add(_innerContent, StringComparer.Ordinal);
}
foreach (var kvp in _attributes)
{
update hash in
{
hash.Add(kvp.Key, StringComparer.Ordinal)
hash.Add(kvp.Value, StringComparer.Ordinal);
}
}
return hash.Build(); That's more verbose than the original. And it's only marginally safer by highlighting buggy code if it's near non-buggy code. To me, I think that tradeof is not worth it. |
Forgetting to write an update block will be just as common as forgetting to assign. |
immutableList.=Add(first); I had syntax like that in mind before, actually. The problem was it seemed pretty awkward because the equals isn't whitespace-separated from the object/method. Although, maybe something like immutableList <= Add(first); would be a viable alternative?
The forward pipe operator will pass the value to the first parameter of the method, not |
That's another good point. This proposal will only benefit cases where you have to do 2+ computations, since you still have to write the variable once at the top of the block and once inside the block; there would be no compelling reason for Closing per the community's feedback on this. |
I think it would be really confusing if |
@svick Whoops, I had forgotten that |
Background
When working with immutable data, it is very common to see code of the form
After each method call,
x
must be reassigned to, asx.SomeOperation()
creates a new object rather than modifying the existing one. This can be a pain point if the variable name is really long, because you have to type it out twice per statement, once to perform an operation on it and once to assign it. This tends to discourage people either from using immutable data, or from giving their variables descriptive names. More importantly, it leads to a class of bugs where people forget to reassign the variable when calling methods with mutable-sounding names, such asstring.Replace
. Here is a real-world bug caused by forgetting to assign the result of an immutable operation.Proposal
We should add
update
blocks to the language. Syntax:The expressions in the
update
block will be evaluated sequentially. After each evaluation, the result of the expression will be assigned to the variable provided afterupdate
(the variable will be updated).Variables can be declared with
update var x in
orupdate ConcreteType x in
at the top of the block.var
is used, the type will be inferred from the GCD of the return types.Empty
update
blocks will be disallowed.Void-returning methods will be disallowed.
If the
update x in
orupdate ConcreteType x in
form is used, all expressions within the block must have a return type matching the type ofx
.The variable will leak outside the scope of the block so you can do things with it after you've finished your computations, in this case
return x
.Nesting of
update
blocks will be permitted:Implications
When working with immutable data, it will be easier to ensure that you don't throw away your results-- just put the variable in an
update
block.Typing time decreases, and people aren't forced to choose between lots of assignment statements (like shown above) or huge expressions (like
var x = CreateImmutableObject().SomeOperation().SomeOperation(). ...
).More examples
The text was updated successfully, but these errors were encountered: