So I have a custom data type Person
data Person = Person{ fname :: String
, lname :: String
, age :: Int
, siblings :: [String]
}
I have a list of this type, foo = [Person].
I'm trying to update a particular Person
. My process is to match their fname
(Assuming each name is unique) and then to update their siblings
values.
addSiblingToPerson :: String -> String -> [Person] -> [Person]
addSiblingToPerson siblingParam fnameParam fooParam =
I'm really struggling to think 'functionally', if I were to do this in an imperative language I could go through each item in [Person]
checking to see if name == fname then this.siblings push newSibling
(Or something along those lines)
I know how to update a record in haskell but I want to return the list of Person
after updating a single person in the collection of Person
.
I just can't wrap my head around how to 'think Haskell'
Thank you :(
You shouldn't think about "updating" something, even though we use that terminology, unless you have a mutable reference and are working in the IO monad. In this situation the thought process should be "how do I compute a new list that is exactly like the previous one except...".
You could either update a single entry or map
a modification function across the entire list. Lets look at the manual, single entry, solution first:
addSiblingToPerson :: String -> String -> [Person] -> [Person]
addSiblingToPerson siblingParam fnameParam allPeople =
case allPeople of
[] -> []
(p:ps) | fname p == fnameParam ->
p { siblings = siblingParam : siblings p } : ps
| otherwise ->
p : addSiblingToPerson siblingParam fnameParam ps
That is, we traverse the list, keeping any non-matching people and updating the first person with a matching fname
, being sure to include the rest of the list.
The map solution is functionally different - it will update all people who share the given fname
and it will traverse the whole list.
addSiblingToPerson :: String -> String -> [Person] -> [Person]
addSiblingToPerson siblingParam fnameParam allPeople =
let update p | fname p == fnameParam = p { siblings = siblingParam : siblings p }
| otherwise = p
in map update allPeople