I have a structure that is instantiated in a parent function and I want to modify that instantiated data with calls to functions from that parent function. Here’s a contrived example:
import Data.List
data MyLists = MyLists {
myInts :: [Int],
myBools :: [Bool]
} deriving (Show)
addIntToList :: Int -> MyLists -> MyLists
addIntToList x main_lists =
main_lists { myInts = Data.List.insert x my_ints }
-- might make a call to another child function that modifies main_list here, and so on (i.e., this is the vertical problem I see with this structuring)
where
my_ints = myInts main_lists
main :: IO()
main = do
let my_main_lists = MyLists [1,2,3] [False, True, False]
let my_new_main_lists = addIntToList 4 my_main_lists
print my_new_main_lists
let my_new_new_main_lists = addBoolToList True my_new_main_lists
print my_new_new_main_lists
-- and so on (this is the lateral problem I see with this code structuring)
What are the alternatives ways to structure this code or accomplish tasks similar to this? Is there a more concise way?
I should add that this gets particularly smelly (ie. code smell) once you’re making a long chain of function calls to child functions; they all end up needing to return a new MyLists or just returning main_list without having done anything to it. That, and the parents might also have to deal with MyList and another return value (e.g., -> (Bool, MyList)).
So, you can imagine a tree structure of function calls all requiring a MyList parameter and return value; that doesn’t seem optimal.
Here’s a more concrete example of the kind of thing I’m talking about. Browse the code at https://github.com/mokehehe/monao (a super mario clone in haskell). You’ll see that state.monad is never used, and that there are upper level structures that must flow throughout the code (e.g., GameGame in Main.hs).
You can make it a little more concise by using the RecordWildCards extension:
The
MyLists{..}pattern dumps the properties of theMyListsrecord into scope. Thus, we can easily reference the oldmyIntswhen initializing the newmyInts.Conversely, if you use
..in expression context, it will fill in uninitialized properties with corresponding names in scope. TheaddIntToListfunction can also be written as:One more cool thing you can do with record wildcards:
Also,
Data.List.insertis a bit verbose. You can just sayinsert, since this import:will import all of the names from
Data.Listinto the namespace. If you don’t like this (e.g. because you want to define a function in your own module calledinsert), you can import it qualified:As far as using
MyListsin a program, theStateTmonad transformer is quite helpful: