You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Conversions of char->decimal, string->nativeint, and string->unativeint #1040
Conversions of char->decimal, string->nativeint, and string->unativeint
leta= char 48m // Sureletb= decimal a // error FS0001: The type 'char' does not support a conversion to the type 'decimal'letc= LanguagePrimitives.ExplicitDynamic<string, nativeint>"2"// Works although warning FS1204: This function is for use by dynamic invocations of F# code and should not be used directlyletd= nativeint "2"// error FS0001: The type 'string' does not support a conversion to the type 'nativeint'lete= LanguagePrimitives.ExplicitDynamic<string, unativeint>"2"// Works although warning FS1204: This function is for use by dynamic invocations of F# code and should not be used directlyletf= unativeint "2"// error FS0001: The type 'string' does not support a conversion to the type 'unativeint'
I propose we stop being inconsistent and add these 3 conversions.
The existing way of approaching this problem in F# is #nowarn "1204", or having to write conversion functions by ourselves.
Pros and Cons
The advantages of making this adjustment to F# are
[?] This is not something which has obviously "already been decided" in previous versions of F#. If you're questioning a fundamental design decision that has obviously already been taken (e.g. "Make F# untyped") then please don't submit it.
Please tick all that apply:
This is not a breaking change to the F# language design
I or my company would be willing to help implement and/or test this
For Readers
If you would like to see this issue implemented, please click the 👍 emoji on this issue. These counts are used to generally order the suggestions by engagement.
For char --> decimal, it's not something I think we should support. The semantic difference between the two spaces is just too far
For string --> nativeint AFAIK .NET doesn't provide us with a suitable parser, and I don't particularly want to include one in FSharp.Core. It's acceptable to parse to Int64 then convert.
@dsyme But FSharp.Core already includes nativeint and unativeint parsers when ExplicitDynamic is called directly.
Yes, you're correct (for others - they parse to int64 and convert using overflow checking)
OK I guess these are ok
@dsyme char->decimal is not ok but decimal->char is fine?
Hmmm. I can see the argument from consistency. Though I don't like either of them TBH.
The intention is F# was never really to treat char as numeric data, even for explicit converstions. We gradually lifted some of the restrictions as compelling use cases arose.
I don't really like admitting more of these, but the argument from consistency wins I guess. However I don't think anyone seriously wants to go from decimal to char or char to decimal without being very explicit about this.
char -> decimal should not have been added in the first place so better to deprecate it than add more functions for consistency.
Current behaviours of the other functions are also fine. No need to support them for user code and it's not the responsibility of F# to provide these things.
char -> decimal should not have been added in the first place so better to deprecate it than add more functions for consistency.
Oh so deprecate char -> float32, char -> float, float -> char and float32 -> char as well? What about the existing conversions to integers?
Current behaviours of the other functions are also fine. No need to support them for user code and it's not the responsibility of F# to provide these things.
Just because you don't need them is not a reason to deny others from having this option.
char -> decimal should not have been added in the first place
I personally agree from an OCaml/ML/how-F#-wants-programming-to-be perspective.
However there is an argument from a different perspective - the System.Decimal is a metadata-defined type, and if we looks at the op_Implicit and op_Explicit for System.Decimal they allow explicit decimal -> char and implicit char -> decimal, so it's reasonable to claim that, because the metadata is there, we should respect that. We would do that for System.Half, for example.
Now F# only treats System.Decimal in a bespoke way, because it allows unit-of-measure on the type, and gives unitized perspective on addition etc. This is one reason inconsistencies crept in - we intercept constraint solving for System.Decimal, but do it imperfectly.
better to deprecate it than add more functions for consistency.
I think in this case deprecation "against the will of the .NET metadata" will only lead us to more inconsistencies. For example we'd need to carefully consider if the .NET metadata conversions would still apply via dotnet/fsharp#10884. (This is a general concern with dotnet/fsharp#10884, that last-resort TDCs via op_Implicit might creep in even for quite foundational types, or metadata-defined foundational types like System.Half. I don't really see how we can avoid that.)
Current behaviours of the other functions are also fine. No need to support them for user code and it's not the responsibility of F# to provide these things.
I think that, given we provide the facility int64 -> string, and given that IntPtr is becoming more and more of a first-class .NET thing (as it should always have been), then it is reasonable to allow nativeint -> string for consistency.
Just because you don't need them is not a reason to ...
FWIW this was not the argument being made, which was more about what FSharp.Core should and shouldn't do. As an aside, let's try to avoid personalising things, and avoid attributing thoughts to others, e.g. try avoid words like "you". I greatly enjoy the differering perspectives people bring to this repo. Let's keep the discussions enjoyable and fun, based on the set of design principles outlined in the notes in this repo - or similar recognised design principles 🙂 🙏 🙂
The original intention of F# was treat decimal as a special, built-in floating point type (basically ignoring built-in metadata on that type because of UoM)
The original intention of F# 1.0 was not to allow the char and floating-point types to interact via conersions
We revised this in F# 2.0 to allow a full matrix of explicit conversions
We didn't revise this for decimal
Given that I think it's right for consistency to include decimal in the matrix.
For string -> nativeint and string -> unativeint
The original .NET types UIntPtr and IntPtr didn't have parsers, so we didn't allow these conversions in F#
Activity
dsyme commentedon Jul 6, 2021
I'm going to decline this one
For
char-->decimal, it's not something I think we should support. The semantic difference between the two spaces is just too farFor
string-->nativeintAFAIK .NET doesn't provide us with a suitable parser, and I don't particularly want to include one in FSharp.Core. It's acceptable to parse to Int64 then convert.Happypig375 commentedon Jul 6, 2021
@dsyme But FSharp.Core already includes nativeint and unativeint parsers when ExplicitDynamic is called directly.
Happypig375 commentedon Jul 6, 2021
@dsyme char->decimal is not ok but decimal->char is fine?
Happypig375 commentedon Jul 6, 2021
Other floating-points are also ok?
dsyme commentedon Jul 6, 2021
Yes, you're correct (for others - they parse to int64 and convert using overflow checking)
OK I guess these are ok
Hmmm. I can see the argument from consistency. Though I don't like either of them TBH.
The intention is F# was never really to treat
charas numeric data, even for explicit converstions. We gradually lifted some of the restrictions as compelling use cases arose.I don't really like admitting more of these, but the argument from consistency wins I guess. However I don't think anyone seriously wants to go from
decimaltocharorchartodecimalwithout being very explicit about this.I'll reopen and approve.
charlesroddie commentedon Jul 6, 2021
char -> decimalshould not have been added in the first place so better to deprecate it than add more functions for consistency.Current behaviours of the other functions are also fine. No need to support them for user code and it's not the responsibility of F# to provide these things.
Happypig375 commentedon Jul 6, 2021
Oh so deprecate
char -> float32,char -> float,float -> charandfloat32 -> charas well? What about the existing conversions to integers?Just because you don't need them is not a reason to deny others from having this option.
dsyme commentedon Jul 6, 2021
I personally agree from an OCaml/ML/how-F#-wants-programming-to-be perspective.
However there is an argument from a different perspective - the System.Decimal is a metadata-defined type, and if we looks at the op_Implicit and op_Explicit for System.Decimal they allow explicit
decimal -> charand implicitchar -> decimal, so it's reasonable to claim that, because the metadata is there, we should respect that. We would do that forSystem.Half, for example.Now F# only treats System.Decimal in a bespoke way, because it allows unit-of-measure on the type, and gives unitized perspective on addition etc. This is one reason inconsistencies crept in - we intercept constraint solving for System.Decimal, but do it imperfectly.
I think in this case deprecation "against the will of the .NET metadata" will only lead us to more inconsistencies. For example we'd need to carefully consider if the .NET metadata conversions would still apply via dotnet/fsharp#10884. (This is a general concern with dotnet/fsharp#10884, that last-resort TDCs via op_Implicit might creep in even for quite foundational types, or metadata-defined foundational types like System.Half. I don't really see how we can avoid that.)
I think that, given we provide the facility
int64 -> string, and given that IntPtr is becoming more and more of a first-class .NET thing (as it should always have been), then it is reasonable to allownativeint -> stringfor consistency.FWIW this was not the argument being made, which was more about what FSharp.Core should and shouldn't do. As an aside, let's try to avoid personalising things, and avoid attributing thoughts to others, e.g. try avoid words like "you". I greatly enjoy the differering perspectives people bring to this repo. Let's keep the discussions enjoyable and fun, based on the set of design principles outlined in the notes in this repo - or similar recognised design principles 🙂 🙏 🙂
dsyme commentedon Jul 6, 2021
To summarise re
charand floating point typesThe original intention of F# was treat decimal as a special, built-in floating point type (basically ignoring built-in metadata on that type because of UoM)
The original intention of F# 1.0 was not to allow the
charand floating-point types to interact via conersionsWe revised this in F# 2.0 to allow a full matrix of explicit conversions
We didn't revise this for decimal
Given that I think it's right for consistency to include decimal in the matrix.
For
string -> nativeintandstring -> unativeintThe original .NET types UIntPtr and IntPtr didn't have parsers, so we didn't allow these conversions in F#
However UIntPtr and IntPtr are becoming more normal as primitive .NET types, for example
System.IntPtr.Parseis available from .NET 5.0 https://docs.microsoft.com/en-us/dotnet/api/system.intptr.parse?view=net-5.0Given that I think it's right to now include these in the matrix.
edgarfgp commentedon Jul 13, 2022
This can be closed as it has been completed here dotnet/fsharp#11681 by @Happypig375