Reputation: 109
I'm working on a beginner Haskell program that can take a database of product prices (i.e. a price list) and a list of barcodes of the items in a shopping cart and generate the list of item prices. But the code that I've written produces type errors that I'll go into below. First, my code:
I begin by defining my relevant types:
type BarCode = Int
type Name = String
type Price = Int
type PriceList = [(BarCode,Name,Price)]
type CartItems = [BarCode]
type CartPrices = [(Name,Price)]
Next, I started to write my main program:
priceCart :: PriceList -> CartItems -> CartPrices
priceCart ((bc, n, p):xs) (b:bs) = map look (xs b)
look :: PriceList -> BarCode -> (Name, Price)
look [] barcode = ("None",0)
look ((bc, n, p):xs) barcode
| bc == barcode = (n, p)
| otherwise = look xs barcode
Essentially, I want to be able to go through my list of barcodes and call my look
function for every element of CartItems
and build a list with the map
function. But my knowledge doesn't quite extend to all of the prelude functions, and so I believe I'm using the map
function incorrectly. In fact, I know this, because of the compile errors that I got (see below). But now I'm also wondering if I'm missing how to correctly iterate through my list in priceCart
-- should I perhaps be using recursion as in my look
function? If so, I'm a little confused as to how I can combine all of the aforementioned functions to produce the desired output.
Upvotes: 1
Views: 412
Reputation: 627
What i think you are looking for is to look PriceList
with the first element in the CartItems
, then look the PriceList
with the second element in the CartItems
and so forth. This can be done with recursion, building a list like this
priceCart :: PriceList -> CartItems -> CartPrices
priceCart [] _ = []
priceCart t@((bc, n, p):xs) (b:bs) = look t b : priceCart t bs
As you see the pattern matching on PriceList is not of use and could be replaced:
priceCart t (b:bs) = look t b : priceCart t bs
Why is this not working with map
in the way you have written in this case?
So map is looking for a function to apply on every element in the input list. Your function takes two arguments, one of the arguments can be found as an element in PriceList, namely xs
. The other argument however, b (BarCode)
, is NOT an element in your list xs
, and map
will not be able to recognize it as one of the arguments for the functions, since it is not an element in the list that you are mapping on.
Upvotes: 2
Reputation: 3081
I think you want to look up each BarCode
in the PriceList
. I expect that the result of priceList
will be the same length as the input CartItems
. This is a good fit for map
.
In general, map :: (a -> b) -> [a] -> [b]
.
In your case, you want map :: (BarCode -> (Name, Price)) -> [BarCode] -> [(Name, Price)]
.
You have a list of type [Barcode]
, so that's no problem. You almost have a function of type BarCode -> (Name, Price)
in look :: PriceList -> BarCode -> (Name, Price)
. The key point is that you will compare each BarCode
to the same PriceList
. So you can write priceCart prices bs = map (look prices) bs
, where look prices :: BarCode -> (Name, Price)
.
Partial application often trips up people learning Haskell. look prices
is equivalent to \barcode -> look prices barcode
in anonymous-function syntax. prices
supplies the first argument to look
, and what's left is a function that still needs a BarCode
.
You can use explicit recursion anywhere that you can use map
- explicit recursion is more general. In this case, I find the version with map
easier to read.
Upvotes: 2