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

Conversions of char->decimal, string->nativeint, and string->unativeint #1040

Closed
4 tasks done
Happypig375 opened this issue Jul 4, 2021 · 10 comments
Closed
4 tasks done

Comments

@Happypig375
Copy link
Contributor

Happypig375 commented Jul 4, 2021

Conversions of char->decimal, string->nativeint, and string->unativeint

let a = char 48m // Sure
let b = decimal a // error FS0001: The type 'char' does not support a conversion to the type 'decimal'

let c = 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 directly
let d = nativeint "2" // error FS0001: The type 'string' does not support a conversion to the type 'nativeint'

let e = 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 directly
let f = 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

  1. Consistency
  2. Convenience
  3. Conciseness

The disadvantages of making this adjustment to F# are that (insert the reason for specifically excluding these 3 conversions as written in https://github.com/dotnet/fsharp/blob/954210d1e8590b708801b2e9f70717f09979b9c0/src/fsharp/ConstraintSolver.fs#L1448-L1451).

Extra information

Estimated cost (XS, S, M, L, XL, XXL): XS

Related suggestions: #1030 - Subtraction of two chars

Affidavit (please submit!)

Please tick this by placing a cross in the box:

  • This is not a question (e.g. like one you might ask on stackoverflow) and I have searched stackoverflow for discussions of this issue
  • I have searched both open and closed suggestions on this site and believe this is not a duplicate
  • [?] 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.

@dsyme
Copy link
Collaborator

dsyme commented 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 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 dsyme closed this as completed Jul 6, 2021
@Happypig375
Copy link
Contributor Author

@dsyme But FSharp.Core already includes nativeint and unativeint parsers when ExplicitDynamic is called directly.

@Happypig375
Copy link
Contributor Author

@dsyme char->decimal is not ok but decimal->char is fine?

@Happypig375
Copy link
Contributor Author

Other floating-points are also ok?

@dsyme
Copy link
Collaborator

dsyme commented Jul 6, 2021

@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.

I'll reopen and approve.

@charlesroddie
Copy link

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.

@Happypig375
Copy link
Contributor Author

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.

@dsyme
Copy link
Collaborator

dsyme commented Jul 6, 2021

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.

image

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 🙂 🙏 🙂

@dsyme
Copy link
Collaborator

dsyme commented Jul 6, 2021

To summarise re char and floating point types

  1. 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)

  2. The original intention of F# 1.0 was not to allow the char and floating-point types to interact via conersions

  3. We revised this in F# 2.0 to allow a full matrix of explicit conversions

  4. 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

  1. The original .NET types UIntPtr and IntPtr didn't have parsers, so we didn't allow these conversions in F#

  2. However UIntPtr and IntPtr are becoming more normal as primitive .NET types, for example System.IntPtr.Parse is available from .NET 5.0 https://docs.microsoft.com/en-us/dotnet/api/system.intptr.parse?view=net-5.0

Given that I think it's right to now include these in the matrix.

@edgarfgp
Copy link
Member

This can be closed as it has been completed here dotnet/fsharp#11681 by @Happypig375

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants