Convert String to Integer/Float in Haskell?
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.
Related videos on Youtube
Ranhiru Jude Cooray
Technophile, Vim Addict, Cryptocurrency Enthusiast
Updated on July 05, 2022Comments
-
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'swords
function.I get the following error which I think is because
makeGroceryItem
accepts aFloat
and anInt
.*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
andc
of typeFloat
andInt
, respectively?-
Martin Fischer over 8 yearsyou have an interesting project. what is it for?
-
-
Nawaz almost 12 years@KennyTM:
read "123.456" :: Float
. What does this syntax mean? What is::
here? Isread
a function? -
kennytm almost 12 years@Nawaz: Yes
read
is a function. Thef :: T
expression forcesf
to have typeT
. -
Nawaz almost 12 years@KennyTM: So the syntax
read "123.456" ::Float
is roughly equivalent tosscanf("123.456", "%f", &fnum);
in C, right? -
kennytm almost 12 years@Nawaz: Only for the
read
function. See stackoverflow.com/questions/5926826/… for detail. -
CMCDragonkai over 9 yearsInternally 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 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.html and hackage.haskell.org/package/base-4.7.0.1/docs/src/… if you are interested.
-
Alex over 8 yearsGreat suggestion! What's your preferred way of extracting the resulting item from the list returned from reads? Two
fst
calls? -
sjakobi over 7 yearsSince base-4.6, there is
readMaybe :: Read a => String -> Maybe a
inText.Read
, which is more convenient than usingreads
in this case.