-
Notifications
You must be signed in to change notification settings - Fork 790
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
Breaking change for DU with [<RequireQualifiedAccess>] formatting in 4.7.2 #9357
Comments
Yes it's weird that this changes with fsharp.core update and not sdk update. |
@forki confusingly (as is always the case with anything related to FSharp.Core), formatting with This is due to #7710, which I'm inclined to keep in. It also changes the output of cases that use |
I don't think it should be reverted. We already changed all our code and I
consider it my own mistake to rely on tostring for output.
But I think we should have communicated it better. I think this will hit a
lot of people.
Phillip Carter <[email protected]> schrieb am Mo., 1. Juni 2020,
20:58:
… @forki <https://github.com/forki> confusingly (as is always the case with
anything related to FSharp.Core), formatting with %A is not actually
implemented in the compiler but is in a file shared with FSharp.Core. So an
FSharp.Core update will lead to these changes.
This is due to #7710 <#7710>, which
I'm inclined to keep in. It also changes the output of cases that use
UseNullAsTrueValue and the formatting of tuples. I think it's a good
change overall, though it is breaking if you depend on the output of %A
for a construct. This is akin to depending on reflection internals though,
which is why I don't feel strongly that this should specifically be
reverted.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#9357 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAAOANHSDQ7OUL6LOPRBME3RUP265ANCNFSM4NP7SEKA>
.
|
It's especially unfortunate that it was changed in a Bugfix release
Steffen Forkmann <[email protected]> schrieb am Mo., 1. Juni 2020, 22:00:
… I don't think it should be reverted. We already changed all our code and I
consider it my own mistake to rely on tostring for output.
But I think we should have communicated it better. I think this will hit a
lot of people.
Phillip Carter ***@***.***> schrieb am Mo., 1. Juni 2020,
20:58:
> @forki <https://github.com/forki> confusingly (as is always the case
> with anything related to FSharp.Core), formatting with %A is not
> actually implemented in the compiler but is in a file shared with
> FSharp.Core. So an FSharp.Core update will lead to these changes.
>
> This is due to #7710 <#7710>, which
> I'm inclined to keep in. It also changes the output of cases that use
> UseNullAsTrueValue and the formatting of tuples. I think it's a good
> change overall, though it is breaking if you depend on the output of %A
> for a construct. This is akin to depending on reflection internals though,
> which is why I don't feel strongly that this should specifically be
> reverted.
>
> —
> You are receiving this because you were mentioned.
> Reply to this email directly, view it on GitHub
> <#9357 (comment)>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/AAAOANHSDQ7OUL6LOPRBME3RUP265ANCNFSM4NP7SEKA>
> .
>
|
@cartermp Another thing to consider - it would probably be nice to have a way of serializing just a union name even when |
I was under the impression that #7710 only addressed A lot of stuff depends on Is it possible that this was inadvertent? I don't see it mentioned in the PR at all, it only seems to address |
It definitely also changed ToString
Abel Braaksma <[email protected]> schrieb am Di., 2. Juni 2020,
00:52:
… I was under the impression that #7710
<#7710> only addressed %A
formatting, but I just noticed that it also changed the default for %O
(on Slack I actually advised someone to use %O without mentioning to
override).
A lot of stuff depends on ToString(). I agree that things shouldn't
depend on %A', which is kinda like asking the compiler to create some
form of useful output, but ToString() should be well-defined.
Is it possible that this was inadvertent? I don't see it mentioned in the
PR at all, it only seems to address %A.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#9357 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAAOANGLK7YWM5XOXJVBMRDRUQWJBANCNFSM4NP7SEKA>
.
|
@abelbraaksma they are the same. type DU = | Case SharpLab gives:
Bad architecture which will have to change as AOT and linkers become more common. |
@charlesroddie, I see, I didn't check the auto-generated code. That suggests that overriding (I habitually add Btw & OT, this default |
The default ToString should ideally not be using reflection. Unfortunately, reflection was introduced rather virally in the past, and it will be a challenge to root it out. |
Ouch, This has the potential to have lots of breaking changes which will only be found at runtime. |
I actually think that this behaviour should be reverted. Regardless of whether people should be using ToString() or not, I see it in the wild often. |
In fact we've just identified one of our projects that has lots of RQA DUs and, yes, we use ToString() on it to generate lookup keys. This would break that catastrophically. |
@isaacabraham I assume you are using DUs with no data, which are like enums. The change to include the type name adds a bit of inconsistency compared to enums. Enums also require qualified access but don't display the type name: type E = | A = 0
E.A.ToString() // "A" I would agree it should be reverted as it doesn't bring any significant advantage and breaks some people. |
I would opt for That would also give us the best of both worlds. |
Yeah just came across this when updating a work project caused all my tests to start failing. I do think it makes sense to include the type when it has the attribute, but at the same time this was not something you'd expect when updating a patch version. |
@cartermp is there a reason why this won't be reverted? |
Yes - although it was a breaking change, it was made and shipped in an LTS of Visual Studio (VS 16.7) and changing it would be another breaking change. Considering the folks here have all reacted to the change, it's just more churn. |
@cartermp that's certainly one way of looking at it. The flipside is:
All IMHO. |
@cartermp |
I would also be for reverting the breaking change. Regarding the point on change being included into LTS version of Visual Studio, I have a couple of points, too.
Whatever the decision here will be, you can already see this was a somewhat sensitive change. So I'd say we have to define this behavior in the spec, and follow the spec in future. Even if you insist on marking this behavior as "undefined" (ah, these C++ vibes) before this change vent live, then we better write a spec for the new behavior. |
The behaviour in 4.7.2 is how I would expect it to work, if there's a require qualified attribute then you would expect the type name to be there. The previous behaviour seem to be a bug to me. |
@7sharp9 I don't think the current behaviour is a big problem, since string functions are broken in F# so shouldn't be relied on, but it doesn't seem consistent to me, because Enums have usage very similar to DUs with RQA but don't print the type name, and using RQA on modules doesn't inject the module name into the ToStrings of contents. type E = | A = 0
E.A.ToString() // "A", not "E.A"
[<RequireQualifiedAccess>]
module M =
type DU = | Case
M.Case.ToString() // doesn't start with "M." |
I don't care either way as long as it's consistent, if we're keeping this behavior IMO we should change anything that uses
@charlesroddie what do you mean? |
@7sharp9 Whether or not it's considered a bug in previous behaviour is more or less irrelevant IMHO. The main thing is that it's a breaking change that's been slipped in without any advance warning. You won't get a compiler break but a runtime error. Imagine you're storing this in a database in a serialised form. The only time you'll realise this is when reading data back in. |
@cartermp Maybe there should be a way to configure this behavior with a parameter, e.g. type RequireQualifiedAccessBehavior = CodeOnly | CodeAndString
[<RequireQualifiedAccess(CodeOnly)>]
type CodeOnlyExample = One | Two
let codeOnlyString = sprintf "%O" CodeOnlyExample.One // yields "One"
[<RequireQualifiedAccess(CodeAndString)>]
type CodeAndStringExample = One | Two
let codeOnlyString = sprintf "%O" CodeAndStringExample .One // yields "CodeAndStringExample.One" As for empty constructor I suggest leave the previous behavior since it's a breaking change. |
Thinking about this some more I think it's best to revert this specifically for the union cases. #10083 is the PR - I'll re-open this until it is merged. |
My comment in #10083 I think we should be more nuanced here
Technically separating 1/2 from 3 is simple. Separating 1 from 2/3 is a little harder (it needs another flag passed through to printf in the format string, the only way to plumb such flags through). |
@dsyme, I agree, in fact, your comment mirrors my observation early in this thread (#9357 (comment)), where I notice that the original issue that caused this was only intended to change I think it makes sense, it's much more common that serialization in part relies on Furthermore, making this distinction allows users choice over both formats, giving easy transition were needed, and both can be independently overridden in DUs and other classes (ie, Currently, the default |
This is very important to do. I've posted an issue - sorry a little long -, that suggests doing this, i.e. generating specialized ToString() code on record/DU types. |
In FSharp.Core 4.7.0 the name of Discriminated Union is ignored in text formatting even if
[<RequireQualifiedAccess>]
is present.However in 4.7.2 this name is added as a prefix for union case.
Repro steps
RequireQualifiedAccess
attribute:%A
as insprintf "%A" ResourceGroup.None
Expected behavior
Snippet above yields
"None"
as it would in4.7.0
Actual behavior
It yields
"ResponseGroup.None"
Known workarounds
ToString()
and use%O
instead of%A
RequireQualifiedAccess
if that's applicable.Related information
The text was updated successfully, but these errors were encountered: