11

Thanks to this excellent tutorial, I know how to read a string (in this case read from a file at people.txt directly into a type synonym:

type Person = [Int]

like this:

people_text <- readFile "people.txt"
let people :: [Person]
    people = read people_text

What I want to do is use a datatype (instead of a type synonym).

Any pointers on what I am missing here? I thought I would be able to read string-data directly into a Person - defined like this (credit to learnyouahaskell.com)

data Person = Person String String Int Float String String deriving (Show)

When I try the obvious

 txt <- readFile "t.txt" (this works OK)

with t.txt containing

"Buddy" "Finklestein" 43 184.2 "526-2928" "Chocolate"

I get this error:

No instance for (Read Person)

3 Answers 3

12

First of all, you need to derive Read for your type.

You can think of read and show as being opposites, and a sort of poor man's serialization. show lets you convert to String, read converts from String, and in most cases the String produced should also be valid Haskell code that, when compiled, produces the same value that read gives you.

On that note, the contents of your file aren't going to work, because that's not the format used by the default implementations of read and show, i.e. the implementations you get by putting Read and Show in the deriving clause.

For example, given this:

data Person = Person String String Int Float String String deriving (Read, Show)

buddy = Person "Buddy" "Finklestein" 43 184.2 "526-2928" "Chocolate"

Then in GHCi, we get:

> show buddy
"Person \"Buddy\" \"Finklestein\" 43 184.2 \"526-2928\" \"Chocolate\""

The quotes are escaped because that's a String value. In the file, it would look like this:

Person "Buddy" "Finklestein" 43 184.2 "526-2928" "Chocolate"

Which you'll note is the same as the original definition in the source file.

0
9

Just add Read to the deriving

 data Person = Person String String Int Float String String deriving (Show, Read)
4
  • 1
    adding Read did the trick. I also had to do this to "read" my Person : xx<- readFile "t.txt" . . .. then . . . let myperson = read xx::Person (without the ::Person at the end it was causing an error). Prolly "obvious' to anyone steeped in Haskell, but not (yet) to me
    – user192127
    Commented May 22, 2011 at 5:14
  • @camccann's answer is good and more comprehensive. It really deserves more upvotes. Commented May 22, 2011 at 16:08
  • 2
    SO voting behaviors are weird, but predictable. Your answer was first, simple, and correct, so got early upvotes (including from me). I answered several minutes later, and about 30min later it was accepted. Likely voters tend to hit questions a lot within the first 5-10min, then a slow stream until an answer is accepted, after which the question is mostly ignored. For simple questions like this, being fast and correct gets more upvotes than being detailed. Commented May 22, 2011 at 16:44
  • Actually, the best rep-building strategy is a quick-draw minimally correct answer, edited to add extra detail while collecting early upvotes. But at this point I'm mostly here to be informative, since I have enough rep for most extra privileges, and my ranking on the all-time Haskell high-score chart is... very unlikely to improve. So, it's kind of you to say that about my answer and I appreciate the sentiment, but I don't mind. :) Commented May 22, 2011 at 16:49
1

"Read" is a typeclass, which means that it makes sense to have a function read :: String -> Person. You can add "read" to the deriving statement, which will automatically generate something sensible for that read function. Note that it would actually require putting "Person" before the various fields ("Buddy", etc).

Alternatively, you could define your own read function:

instance Read Person where 
  read str = undefined -- add your definition here

Not the answer you're looking for? Browse other questions tagged or ask your own question.