Right now I have two types:
type Rating = (String, Int)
type Film = (String, String, Int, [Rating])
I have a file that has this data in it:
"Blade Runner"
"Ridley Scott"
1982
("Amy",5), ("Bill",8), ("Ian",7), ("Kevin",9), ("Emma",4), ("Sam",7), ("Megan",4)
"The Fly"
"David Cronenberg"
1986
("Megan",4), ("Fred",7), ("Chris",5), ("Ian",0), ("Amy",6)
How can I look through then file storing all of the entries into something like FilmDatabase = [Film] ?
Is this homework?
You might find these functions useful:
readFile :: FilePath -> IO String
lines :: String -> [String]
break :: (a -> Bool) -> [a] -> ([a], [a])
dropWhile :: (a -> Bool) -> [a] -> [a]
null :: [a] -> Bool
read :: Read a => String -> a
Remember that
String
is the same as[Char]
.Some clues:
dropWhile null
will get rid of empty lines from the start of a listbreak null
will split a list into the leading run of non-empty lines, and the rest of the listHaskell has a great way of using the types to find the right function. For instance: In Gregs answer, he wants you to figure out (among other things) how to convert the year of the film from a String to an Int. Well, you need a function. What should be the type of that function? It takes a String and returns an Int, so the type should be
String -> Int
. Once you have that, go to Hoogle and enter that type. This will give you a list of functions with similar types. The function you need actually has a slightly different type -Read a => String -> a
- so it is a bit down the list, but guessing a type and then scanning the resulting list is often a very useful strategy.Haskell provides a unique way of sketching out your approach. Begin with what you know
Attempting to load this program into ghci will produce
It needs to know what
readFilms
is, so add just enough code to keep moving.It is a function that should do something related to
Film
data. Reload this code (with the:reload
command or:r
for short) to getThe type of
print
isIn other words,
print
takes a single argument that, informally, knows how to show itself (that is, convert its contents to a string) and creates an I/O action that when executed outputs that string. It’s more-or-less how you expectprint
to work:We know that we want to
print
theFilm
data from the file, but, although good, ghc can’t read our minds. But after adding a type hintwe get a new error.
The error tells you that the compiler is confused about your story. You said
readFilms
should give it back aFilm
, but the way you called it inmain
, the computer should have to first perform some I/O and then give backFilm
data.In Haskell, this is the difference between a pure string, say
"JamieB"
, and a side effect, say reading your input from the keyboard after prompting you to input your Stack Overflow username.So now we know we can sketch
readFilms
asand the code compiles! (But we can’t yet run it.)
To dig down another layer, pretend that the name of a single movie is the only data in
ratings.dat
and put placeholders everywhere else to keep the typechecker happy.This version compiles, and you can even run it by entering
main
at the ghci prompt.In dave4420’s answer are great hints about other functions to use. Think of the method above as putting together a jigsaw puzzle where the individual pieces are functions. For your program to be correct, all the types must fit together. You can make progress toward your final working program by taking little babysteps as above, and the typechecker will let you know if you have a mistake in your sketch.
Things to figure out:
String
) to anInt
to cooperate with your definition ofFilm
?readFilms
accumulate and return a list ofFilm
data?