Search code examples
haskelllookupalgebraic-data-typescustom-data-typenon-exhaustive-patterns

Why is Haskell lookup function causing Non-Exhaustive pattern error on custom data type?


Anyone know why this is causing the error Non-exhaustive patterns in function getCityPopulation?

type Name = String
type Coordinates = (Int, Int)
type Pop = Int
type TotalPop = [Pop]
type City = (Name, (Coordinates, TotalPop))

testData :: [City]
testData = [("New York City", ((1,1), [5, 4, 3, 2])),
           ("Washingotn DC", ((3,3), [3, 2, 1, 1])),
           ("Los Angeles", ((2,2), [7, 7, 7, 5]))]

getCityPopulation :: [City] -> Name -> Int -> Maybe (Coordinates, TotalPop)
getCityPopulation [] nameIn yearIn = error "Can't be empty"
getCityPopulation [cs] nameIn yearIn
    | nameIn == "" = error "Input City name"
    | yearIn == 0 || yearIn < 0 = error "invalid year"
    | otherwise = lookup nameIn [cs]

As you can see, I've tried to add a case for when any of the parameters may be empty or just invalid for the lookup function. What else could it be?

Also, I know the yearIn variable is redundant at the moment, it will be pertinent later, for the intended function use, which will be to get the yearIn element of the TotalPop list.

Thanks in advance for any and all help that is given :)


Solution

  • [cs] there means a list of one city (list-item) bound to cs - I think you want this - so there is no case for lists with more than zero or one elements - the error is telling you that.

    getCityPopulation :: [City] -> Name -> Int -> Maybe (Coordinates, TotalPop)
    getCityPopulation [] nameIn yearIn = error "Can't be empty"
    getCityPopulation cs nameIn yearIn
        | nameIn == "" = error "Input City name"
        | yearIn == 0 || yearIn < 0 = error "invalid year"
        | otherwise = lookup nameIn cs
    

    now the 3rd entry matches any list and binds it to cs but as the case before already catched empty-lists cs will have at least one element.

    Btw: I don't think you need this - why throw errors (crash the program) when you are returning a Maybe anyway? - also you never use yearIn aside from checking it.

    I'd recommend just

    tryGetCityPopulations :: [City] -> Name -> Maybe TotalPop
    tryGetCityPopulations cs nameIn = snd <$> lookup nameIn cs
    

    this way the function does what the name implies and you can go on with the result as you see fit.