Jackson Jacob
Jackson Jacob

Reputation: 119

Tuple comprehension Haskell

The end goal is to get a list of ratings such that no same user should be duplicated in the list. Therefore you cannot have "Emma" rating twice.

Data and Tuple defined as:

type Rating = (String, Int)
data Film = Film {  title :: String,
                    director :: String,
                    year :: Int,
                    ratings :: [Rating] } deriving (Read,Show)

old function defined as (Which works):

newRatings :: [Rating] -> Rating -> [Rating]
newRatings oldRatings (newUser,newRate) = [(user,rate) | (user,rate) <- oldRatings, user /= newUser] ++ [(newUser,newRate)]

creating a better function defined as:

newRating :: Rating -> [Rating] -> [Rating]
newRating (newUser,newRate) = ((newUser,newRate): ) . filter(((map fst ratings)/= newUser). ratings)

I don't understand where I am going wrong. My understanding of the function is. -> Get the rating and compare it the an old list of ratings to check if the users match. If they do, don't include their specific rating and carry on passing list until empty. Then append the new rating to the list.

Could someone explain where I am going wrong?

The Error I'm getting:

 v3.hs:104:63:
    Couldn't match expected type ‘[Rating] -> Bool’
                with actual type ‘Bool’
    In the first argument of ‘(.)’, namely
      ‘((map fst ratings) /= newUser)’
    In the first argument of ‘filter’, namely
      ‘(((map fst ratings) /= newUser) . ratings)’
    In the second argument of ‘(.)’, namely
      ‘filter (((map fst ratings) /= newUser) . ratings)’

v3.hs:104:72:
    Couldn't match expected type ‘[(Char, b0)]’
                with actual type ‘Film -> [Rating]’
    Probable cause: ‘ratings’ is applied to too few arguments
    In the second argument of ‘map’, namely ‘ratings’
    In the first argument of ‘(/=)’, namely ‘(map fst ratings)’

v3.hs:104:93:
    Couldn't match type ‘(String, Int)’ with ‘Film’
    Expected type: Rating -> [Rating]
      Actual type: Film -> [Rating]
    In the second argument of ‘(.)’, namely ‘ratings’
    In the first argument of ‘filter’, namely
      ‘(((map fst ratings) /= newUser) . ratings)’

Upvotes: 1

Views: 871

Answers (1)

amalloy
amalloy

Reputation: 91837

First: when asking why something ins't working, please provide a paste of what is actually happening: you would get better help more quickly if you included the compiler error message, so prospective answerers don't have to copy and paste your code to see what's wrong.

The first thing that jumps out at me is that

(map fst ratings)/= newUser

doesn't look well-typed at all: newUser is a String, so it can't possibly be equal to (map fst ratings), a list of strings. You want something more like:

filter ((newUser /=) . fst) . ratings

Another problem is that you've made this point-free: that's cool sometimes, but here I think it's making it a lot harder to read. It's much simpler to write like this:

uniquify :: [Rating] -> [Rating]
uniquify [] = []
uniquify (rate@(newUser,_):more) = rate : uniquify (filter ((newUser /=) . fst) more)

But better still is to find an appropriate built-in function, and in this case that's Data.List.nubBy, combined with Data.Function.on:

uniquify' :: Eq a => [(a,b)] -> [(a,b)]
uniquify' = nubBy ((==) `on` fst)

That's a more general type, which you can specialize to the type given to uniquify, and a lot simpler to implement.

Upvotes: 2

Related Questions