I have a data type
data Time = Time {hour :: Int,
minute :: Int
}
for which i have defined the instance of Show as being
instance Show Time where
show (Time hour minute) = (if hour > 10
then (show hour)
else ("0" ++ show hour))
++ ":" ++
(if minute > 10
then (show minute)
else ("0" ++ show minute))
which prints out times in a format of 07:09.
Now, there should be symmetry between Show and Read, so after reading (but not truly (i think) understanding) this and this, and reading the documentation, i have come up with the following code:
instance Read Time where
readsPrec _ input =
let hourPart = takeWhile (/= ':')
minutePart = tail . dropWhile (/= ':')
in (\str -> [(newTime
(read (hourPart str) :: Int)
(read (minutePart str) :: Int), "")]) input
This works, but the "" part makes it seem wrong. So my question ends up being:
Can anyone explain to me the correct way to implement Read to parse "07:09" into newTime 7 9 and/or show me?
I’ll use
isDigitand keep your definition of Time.You used but didn’t define
newTime, so I wrote one myself so my code compiles!Firstly, your show instance is a little wrong because
show $ Time 10 10gives"010:010"Let’s have a look at
readsPrec:That’s a parser – it should return the unmatched remaining string instead of just
"", so you’re right that the""is wrong:It can’t parse it because you threw away the
,23:12,03:22]in the first read.Let’s refactor that a bit to eat the input as we go along:
Gives for example
It does, however, allow “3:45” and interprets it as “03:45”. I’m not sure that’s a good idea, so perhaps we could add another test
length hours == 2.I’m going off all this split and span stuff if we’re doing it this way, so maybe I’d prefer:
Which actually seems cleaner and simpler to me.
This time it doesn’t allow
"3:45":