Convert String to Integer/Float in Haskell?

105,622

Solution 1

read can parse a string into float and int:

Prelude> :set +t
Prelude> read "123.456" :: Float
123.456
it :: Float
Prelude> read "123456" :: Int
123456
it :: Int

But the problem (1) is in your pattern:

createGroceryItem (a:b:c) = ...

Here : is a (right-associative) binary operator which prepends an element to a list. The RHS of an element must be a list. Therefore, given the expression a:b:c, Haskell will infer the following types:

a :: String
b :: String
c :: [String]

i.e. c will be thought as a list of strings. Obviously it can't be read or passed into any functions expecting a String.

Instead you should use

createGroceryItem [a, b, c] = ...

if the list must have exactly 3 items, or

createGroceryItem (a:b:c:xs) = ...

if ≥3 items is acceptable.

Also (2), the expression

makeGroceryItem a read b read c

will be interpreted as makeGroceryItem taking 5 arguments, 2 of which are the read function. You need to use parenthesis:

makeGroceryItem a (read b) (read c)

Solution 2

Even though this question already has an answer, I strongly suggest using reads for string conversion, because it's much safer, as it does not fail with an unrecoverable exception.

reads :: (Read a) => String -> [(a, String)]

Prelude> reads "5" :: [(Double, String)]
[(5.0,"")]
Prelude> reads "5ds" :: [(Double, String)]
[(5.0,"ds")]
Prelude> reads "dffd" :: [(Double, String)]
[]

On success, reads returns a list with exactly one element: A tuple consisting of the converted value and maybe unconvertable extra characters. On failure, reads returns an empty list.

It's easy to pattern-match on success and failure, and it will not blow up in your face!

Solution 3

Two things:

createGroceryItem [a, b, c] = makeGroceryItem a (parse b) (parse c)
-- pattern match error if not exactly 3 items in list

or alternatively

createGroceryItem (a : b : c : _) = makeGroceryItem a (parse b) (parse c)
-- pattern match error if fewer than 3 items in list, ignore excess items

because : is not the same as ++.

Meanwhile on the right hand side --- the side that's giving you the error message you see --- you have to group expressions using brackets. Otherwise parse is interpreted as being a value you want to pass to makeGroceryItem, so the compiler complains when you try to pass 5 arguments to a function that only takes 3 parameters.

Share:
105,622

Related videos on Youtube

Ranhiru Jude Cooray
Author by

Ranhiru Jude Cooray

Technophile, Vim Addict, Cryptocurrency Enthusiast

Updated on July 05, 2022

Comments

  • Ranhiru Jude Cooray
    Ranhiru Jude Cooray almost 2 years
    data GroceryItem = CartItem ItemName Price Quantity | StockItem ItemName Price Quantity
    
    makeGroceryItem :: String -> Float -> Int -> GroceryItem
    makeGroceryItem name price quantity = CartItem name price quantity
    
    I want to create a `GroceryItem` when using a `String` or `[String]`
    
    createGroceryItem :: [String] -> GroceryItem
    createGroceryItem (a:b:c) = makeGroceryItem a b c
    

    The input will be in the format ["Apple","15.00","5"] which I broke up using Haskell's words function.

    I get the following error which I think is because makeGroceryItem accepts a Float and an Int.

    *Type error in application
    *** Expression     : makeGroceryItem a read b read c
    *** Term           : makeGroceryItem
    *** Type           : String -> Float -> Int -> GroceryItem
    *** Does not match : a -> b -> c -> d -> e -> f*
    

    But how do I make b and c of type Float and Int, respectively?

    • Martin Fischer
      Martin Fischer over 8 years
      you have an interesting project. what is it for?
  • Nawaz
    Nawaz almost 12 years
    @KennyTM: read "123.456" :: Float . What does this syntax mean? What is :: here? Is read a function?
  • kennytm
    kennytm almost 12 years
    @Nawaz: Yes read is a function. The f :: T expression forces f to have type T.
  • Nawaz
    Nawaz almost 12 years
    @KennyTM: So the syntax read "123.456" ::Float is roughly equivalent to sscanf("123.456", "%f", &fnum); in C, right?
  • kennytm
    kennytm almost 12 years
    @Nawaz: Only for the read function. See stackoverflow.com/questions/5926826/… for detail.
  • CMCDragonkai
    CMCDragonkai over 9 years
    Internally how does Read actually work? Does it break up the string as a list of Char. Then does it have a dictionary from char to int, and the compare the list of char to to the dictionary in order to get the corresponding int?
  • kennytm
    kennytm over 9 years
    @CMCDragonkai: It reads all digits ('0'–'9'), and the convert it to decimal (using 1234 = 123×10 + 4 like all other languages). The actual implementation is quite complicated as it is also generic to support e.g. reading a hex number or reading to a list of integers. See hackage.haskell.org/package/base-4.7.0.1/docs/src/GHC-Read.h‌​tml and hackage.haskell.org/package/base-4.7.0.1/docs/src/… if you are interested.
  • Alex
    Alex over 8 years
    Great suggestion! What's your preferred way of extracting the resulting item from the list returned from reads? Two fst calls?
  • sjakobi
    sjakobi over 7 years
    Since base-4.6, there is readMaybe :: Read a => String -> Maybe a in Text.Read, which is more convenient than using reads in this case.