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

Safe versions for built-in number conversion functions #749

Open
Happypig375 opened this issue Jun 11, 2019 · 9 comments

Comments

Projects
None yet
7 participants
@Happypig375
Copy link
Contributor

commented Jun 11, 2019

Safe versions for built-in number conversion functions

I propose we add safe versions for built-in number conversion functions.

The existing way of approaching this problem in F# is writing these functions yourself.

e.g. tryInt which returns int option rather than int which throws exceptions. Consider int "Not a number" which throws System.FormatException vs tryInt "Not a number" which returns None.

They would also check if the value being converted is out of range, e.g. tryByte 333 = None instead of byte 333 = 77uy.

Pros and Cons

The advantages of making this adjustment to F# are

  1. eliminating the need to awkwardly call System.Int32.TryParse.
  2. preventing bugs by garbage data that are caused by numbers out of range.
  3. discouraging exceptions from the current number conversion functions.
  4. encouraging options instead of bool and number tuples, which are non-idiomatic and confusing.
  5. making F# even more simpler to get started, no need to deal with exceptions, static members, etc. just to convert a number.

The disadvantage of making this adjustment to F# is more functions to learn.

Extra information

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

Related suggestions: (put links to related suggestions here)
#740

Unresolved question

ValueOption or Option???

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

This comment has been minimized.

Copy link
Member

commented Jun 11, 2019

Generally I'm not opposed. Thoughts @dsyme ?

How often do you find yourself needing something like this and being annoyed by having to write a pattern match expression instead?

@7sharp9

This comment has been minimized.

Copy link
Member

commented Jun 11, 2019

@theprash

This comment has been minimized.

Copy link

commented Jun 11, 2019

I always wish that the int, float etc. functions already worked like this and the unsafe versions where called unsafeInt, unsafeFloat etc.

Here's another thought: does int try to do too much? All the possible values of int16 exist inside int, so int16 to int is a safe conversion. But string to int definitely isn't. Why is one function trying to handle all of these cases if we care about encouraging strong static typing? We could have Int.ofString and Int.ofInt64 returning an int option or int result. And Int.ofInt16 safely returning an int.

Why do we try to effectively unsafely parse/cast values into an int as the idiomatic default, but not do the same with arrays. There's no array function that tries to parse a string into an array and throws an exception if it can't. Just because arrays aren't primitive values? 🤷🏾‍♂️

@Happypig375

This comment has been minimized.

Copy link
Contributor Author

commented Jun 13, 2019

I don’t think this is needed as we get the out param mapped to a bool in a
pattern match, that’s works quite nicely.

What about safety when converting int64 to int32?

@yatli

This comment has been minimized.

Copy link

commented Jun 14, 2019

This proposal is actually about two things, number parsing and number conversion.
For the parsing part, I'd really love to see tryInt and alike.

For value conversion, we should possibly stick with --checked semantics.
Or end up with "try+", "try-" etc. for arithmetics, to be fair (of course, no!)

@BentTranberg

This comment has been minimized.

Copy link

commented Jun 20, 2019

Does built-in in this case mean part of a library, or does it mean that the compiler itself is expanded with functionality? I'm asking because I would generally be skeptical to adding this sort of functionality to a compiler if it might just as well be in a library. But I know far too little about the F# compiler to have opinions.

@Happypig375

This comment has been minimized.

Copy link
Contributor Author

commented Jun 20, 2019

This should be in FSharp.Core, which is the F# standard library, like with the existing int, byte... functions. You can visit the current standard library at https://github.com/dotnet/fsharp/tree/master/src/fsharp/FSharp.Core.

@BentTranberg

This comment has been minimized.

Copy link

commented Jun 20, 2019

I find myself writing and snipping these few wrapper functions for Xxx.TryParse and maybe some other, over and over again, for new solutions and LINQPad scripts. I imagine I'm not the only one. So yes, it would be nice not having to do that anymore.

"But what about second breakfast?" asked Pippin.

I live in Norway, and currently write software dominantly for the local market. I frequently need to read text with numeric data. These numbers sometimes use comma as decimal separator, and sometimes period, and sometimes I have to cater for both possibilities with a bit of trickery. The localization settings of the computer is irrelevant, because they can be quite random and you never know when the user changes them or for what reason. That's how the real world is, and I can't do anything about it. So to solve the task properly, I can't normally use int, but rather have to call the overloaded Int32.TryParse to force a desired interpretation.

This means that I'd like the tryInt and siblings to have the same possibilities as Int32.TryParse with overloaded siblings. We can't overload functions, but we can create e.g. tryIntBlah (where Blah is something nice I'm not smart enough to think of right now) that takes an additional Globalization.NumberStyles. As the first parameter, so piping is nice.

@dsyme

This comment has been minimized.

Copy link
Collaborator

commented Jul 1, 2019

Generally I'm not opposed. Thoughts @dsyme ?

Likewise. If we add them to FSharp.Core it seems they should exactly correspond to int and friends, with Operators.Checked the default.

It's tricky though

  1. the concern of @BentTranberg is real, in that we often want more sophisticated parsing.

  2. floating point will always be a PITA - would we return None if NaN would have resulted? I presume not

  3. The operators int and friends are simulated by the F# compiler (via SRTP constraints) and require both library and compiler logic to make them extensible for new types. It can be a little daunting to get this right for the whole matrix of possibilities.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.