gbm0102
gbm0102

Reputation: 109

Haskell - cycling through list to map new lists

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.

Compile Errors

Upvotes: 1

Views: 412

Answers (2)

JohEker
JohEker

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

bergey
bergey

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

Related Questions