views:

724

answers:

3
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 words function in haskell. I get this error which i think is because the makeGroceryItem accepts a Float and an Int. But how do i make b and c Float and Int respectively?

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

Thanx a lot in advance :)

+5  A: 

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 functions. You need to use parenthesis:

makeGroceryItem a (read b) (read c)
KennyTM
Thank you Sir! It worked fine :)
Ranhiru Cooray
+2  A: 

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.

Dave Hinton
Thank you to too, for clarifying my doubts :)
Ranhiru Cooray
A: 

Even though this is done already, 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 it will return a list with exactly one element, and a tuple consisting of the converted value and maybe unconvertable extra characters, and on failure an empty list. It's easy to pattern match for success and failure, and it will not blow up in your face!

LukeN