JamieB
JamieB

Reputation: 923

Modify and return list with modified element

I am trying to modify an element in the list in the database to add to ratings, I want to return a modified database with the new element in it. I understand Haskell has immutable things but at the same time I can't quite grasp how to do it.

Here are the types:

data Film = Film Director Title Year Ratings
     deriving (Show,Ord,Eq, Read)

testDatabase :: [Film]

The only code I have is:

--addRating :: Rating -> Film -> Film
--addRating rating (Film name director year ratings)= (Film name director year [(ratings : rating)])


--findFilm name = head $ filter (\(Film n _ _ _) -> n == name) testDatabase 

The find film works well but I can't get the addRating to work, even if it did work I still don't understand how to mesh it all together to have a function to call to return a list of Film that has the element with the new ratings on it.

Upvotes: 1

Views: 397

Answers (4)

bstpierre
bstpierre

Reputation: 31206

The key bit of understanding to "immutable things" is that you don't need to modify the thing that you've been given, you just create a "copy" that's a little different and return that as the result. For example, you've got a list l that contains [1,2,3]. When you do l ++ [4], you get back [1,2,3,4], but the value of l hasn't changed.

Use the same principle with your film records -- you're already close.

As others have pointed out, you've got your types mixed up in the (ratings : rating) expression. To use :, the element goes in front and the list goes in back. You could switch your types around, but that will put the new rating at the front, which you may not want. An alternative would be to use ++, which appends two lists. So to use ++, you need to make a list out of the single element, and append it to the existing ratings.

addRating :: Rating -> Film -> Film
addRating rating (Film name director year ratings) =
    (Film name director year (ratings ++ [rating]))

Upvotes: 0

porges
porges

Reputation: 30580

You almost had it correct:

addRating rating (Film name director year ratings)
    = (Film name director year [(ratings : rating)])

At the moment you have the code:

[(ratings : rating)]

The : operator has type a -> [a] -> [a], it takes one element and appends it to the start of the list, not the end of the list, so you need to switch your arguments around.

Also, the syntax [x], when used like you have, creates a list consisting of one element. If you use it here it will take the list you have created and wrap it inside another list, which is not what you want.

You just need to make a list consisting of the rating prepended to the rest of the ratings:

(rating : ratings)

Upvotes: 3

amindfv
amindfv

Reputation: 8448

... I can't get the addRating to work, even if it did work I still don't understand how to mesh it all together to have a function to call to return a list of Film that has the element with the new ratings on it.

It seems like you'd like to have a function which returns the new data, in a way that can be accessed "where the old data used to be."

The key thing to understand here is that that alone would make it mutable data.

One thing that functional programming offers is the guarantee that data will always be the same. In an imperative (non-functional) language, if you have several processes which might change data, you have to be careful that they don't modify each others' data in unexpected ways. Immutable data makes this a non-issue.

If you truly would like destructive updates, there are ways to do that. However, probably what you're looking for is the State monad, which is a nice way to "carry around" state without destructive updates.

Upvotes: 0

applicative
applicative

Reputation: 8199

It is as Porges said, you got rating and ratings in the wrong order. You might find the record syntax approach more to your liking:

data Film = Film {director :: Director, title :: Title, year :: Year, ratings :: Ratings}
                deriving (Show,Ord,Eq, Read)

addRating :: Rating -> Film -> Film
addRating rating film = film {ratings = rating : ratings film}

Here, film {director = "Martin Scorsese"} 'updates' the director field, that is, the expression refers to a Film with a different director, but everything else the same. In the present case, though, you need the contents of the old ratings field, so you need to prepend rating to ratings film. This way of declaring the type gives the derive Show a different look.

Upvotes: 2

Related Questions