2

I use read "123" :: Int to read in Int from String. This works great for a simple case such as "123"; But it fails when the input is "12.34".

What do people usually do in such cases?

Edit: I also would like to know if there is a simple way to parse numbers with thousand separator such as "12,345"

asked Oct 28, 2019 at 12:17
5
  • 3
    12.34 is not an Int. You can parse it to a Float, or a Decimal. Commented Oct 28, 2019 at 12:18
  • 2
    I feel like a lot of people answered without even knowing what the question was. What result do you want when given the input "12.34"? 12? 1234? Program dies? An error that you can dispatch on but doesn't kill the program? A split of the string into "12" and ".34"? ...something else? Commented Oct 28, 2019 at 13:37
  • What about "12,343" with a thousand seperator? Commented Oct 28, 2019 at 14:23
  • What if I told you that in some parts of the world, the . in 12.345 is also a thousands separator? So I ask again: what result do you want when given the input "12.345"? Commented Oct 28, 2019 at 15:48
  • Sorry for not being clear. My question is how to parse Int when given a string containing separators. Anyway, I see that I could simply use a filter to get rid of the separators. Thanks Commented Oct 29, 2019 at 8:34

2 Answers 2

6

This does not work, since 12.34 is not an Integer. You can parse this as a Float or a Decimal for example:

Prelude> read "12.34" :: Float
12.34
answered Oct 28, 2019 at 12:24

Comments

2

Read a floating point number.

Consider reading a floating point number first, then rounding it as fits your application.

λ (round . read) "12.34" 
12

Note that it can read a number without a dot as well:

λ (round . read) "12" 
12

There are more rounding methods.

Prelude offers several functions for transforming fractions into integral numbers: beside the obvious round, there are floor, ceiling and truncate. There are subtleties to them.

Rounding methods have polymorphic powers.

If you enable -Wtype-defaults, you will see that the number "12.34" is read as a Double and then transformed to Integer. If you inspect round though, you will see that it is quite general:

λ :type round
round :: (RealFrac a, Integral b) => a -> b

So, for instance, you may retrieve an Int.

λ (round . read) "12.34" :: Int
12

You can give type arguments to your functions.

You can use the TypeApplications language extension to get concise and flexible code. For instance, if you wish to parse an exotic looking Rational to a Word8:

λ round @_ @Word8 . read @Rational $ "2833 % 11"
2

You can avoid run time errors.

You can get a run time error from any of the above, if someone supplies gibberish. One tiny typo is enough to bring the whole program down. And the problem is remedied already: we now have readMaybe in Prelude. See:

λ fmap round . readMaybe @Rational $ "339 % 108"
Just 3
λ fmap round . readMaybe @Rational $ "3.14159"
Nothing

I think readMaybe should be preferred to read whenever possible.

Limitations of instance Read of basic number types.

The Read instances available in Prelude are geared towards the lexical syntax of Haskell, which means there is a single unified reading. It supports some notational conventions, like exponential notation, by default, optionally admits some more, like binary literals, and eschews others. For example, omitting 0 before the decimal separator is widely accepted in some countries, but it is not supported by any Read instance in base. The use of thousands separators, while it greatly facilitates the human understanding of large numbers, is, to my knowledge, also ignored. There is no effort whatsoever to support various regional standards and conventions in base, and, as far as I know, there is no "go-to" library to achieve more flexible support for parsing numbers at the moment.

So, if you require something "unusual" — that is, exceeding the Haskell standard — the advice is twofold:

  • Use a parsing library to construct a specialized parser. There is a parser combinator module in base, along with a wide choice of advanced and modern parsing libraries on Hackage.
  • Use a simple hack. For instance, if you wish to support thousands separators, you may do something like filter (/= ','). Some widely used tools in Haskell are built on hacks of this sort.

In the latter case, you should beware of possible ambiguity. For example, if you simply remove all non-number characters from the input string, you will cripple hexadecimals and exponential notation:

λ (round . read) "2.6356e5" 
263560
λ (round . read . filter isNumber) "2.6356e5"
263565

I wish one day we will have stronger localization support. I think this comes with wide adoption, and Haskell is yet a local language.

answered Oct 28, 2019 at 12:31

3 Comments

Thanks for taking the time to reply. What about a number format with a thousand seperator such as "12,345" ?
@McBearHolden This is not optimistic. I updated my answer. In short, use filter (/= ',').
Thanks. Adding a filter function is the simplest one.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.