Convert String to Integer/Float in Haskell?

HaskellFloating PointInt

Haskell Problem Overview


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?

Haskell Solutions


Solution 1 - Haskell

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 - Haskell

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 - Haskell

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.

Solution 4 - Haskell

filterNumberFromString :: String -> String
filterNumberFromString s =
    let allowedString = ['0'..'9'] ++ ['.', ',']
        toPoint n
            | n == ',' = '.'
            | otherwise = n

        f = filter (`elem` allowedString) s
        d = map toPoint f
    in d


convertStringToFloat :: String -> Float
convertStringToFloat s =
    let betterString = filterNumberFromString s
        asFloat = read betterString :: Float
    in asFloat

print (convertStringToFloat "15,00" + 1)

-> prints 16.0

Thats how I solved this task in my project.

Solution 5 - Haskell

readMaybe can be used for this. It is also a total function as opposed to read (which could throw an exception).

Prelude> import Text.Read
Prelude Text.Read> readMaybe ("1.5") :: Maybe Float
Just 1.5

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionRanhiru Jude CoorayView Question on Stackoverflow
Solution 1 - HaskellkennytmView Answer on Stackoverflow
Solution 2 - HaskellLukeNView Answer on Stackoverflow
Solution 3 - Haskelldave4420View Answer on Stackoverflow
Solution 4 - HaskellchrisheynView Answer on Stackoverflow
Solution 5 - HaskellChris StryczynskiView Answer on Stackoverflow