Reputation: 2721
I implemented these (simplified) datatypes in a Haskell program :
data Type = ValA
| Valb
| ValC
data Prefix = PrefA
| PrefB
| PrefC
| NoPref
data Combin = Combin Prefix Type
instance Show Type where
show ValA = "typeA"
show Valb = "O"
show ValC = "mp"
instance Read Type where
readsPrec _ "typeA" = [(ValA,"")]
readsPrec _ "O" = [(Valb,"")]
readsPrec _ "mp" = [(ValC,"")]
instance Show Prefix where
show PrefA = "a"
show PrefB = "bm"
show PrefC = "c"
show NoPref = ""
instance Read Prefix where
readsPrec _ "a" = [(PrefA,"")]
readsPrec _ "bm" = [(PrefB,"")]
readsPrec _ "c" = [(PrefC,"")]
readsPrec _ "" = [(NoPref,"")]
instance Show Combin where
show (Combin pre typ) = show pre++show typ
With the instances, I'm able to show
and read
the types Prefix
and Type
. The Combin
datatype is a concatenation of a Prefix
and a Type
.
Now, I would like to implement the read instance for the Combin
datatype and I have no idea on how to do it.
I thought about deriving the Combin type, but it cause the output string of Combin PrefA ValC to be "Combin a mp". And it's not what I want. I want "amp" concatened together. Same thing for read
I thought about making pattern matching with the input string but the Prefix
strings have differents length and may be void (NoPref
).
Have you ever implemented a such functionnality with read ? Do you know how to do it ?
Upvotes: 0
Views: 91
Reputation: 27626
Your readsPrec
implementations are wrong, since they are supposed to accept valid prefixes of the input, not necessarily consume the whole input. Your readsPrec
functions are not composable.
The key to the solution is to rewrite them so that they check if their input matches any of the names:
import Data.List (stripPrefix)
instance Read Type where
readsPrec _ s | Just s' <- stripPrefix "typeA" s = [(ValA, s')]
| Just s' <- stripPrefix "O" s = [(Valb, s')]
| Just s' <- stripPrefix "mp" s = [(ValC, s')]
| otherwise = []
and similarly for Prefix
:
instance Read Prefix where
readsPrec _ s | Just s' <- stripPrefix "a" s = [(PrefA, s')]
| Just s' <- stripPrefix "bm" s = [(PrefB, s')]
| Just s' <- stripPrefix "c" s = [(PrefC, s')]
| otherwise = [(NoPref, s)]
Note the last branch of readsPrec
for Prefix
, which says that every string that doesn't begin with "a"
, "bm"
or "c"
is to be parsed as NoPref
. This only works for Combin
because there's no Type
which starts with any of the Prefix
bits; otherwise, the parser for Prefix
would need to be nondeterminstic, since a string like "aXY"
could either correspond to a Prefix
of "a"
and a Type
of "XY"
, or a Prefix
of NoPref
and a Type
of "aXY"
. I went with the deterministically eager version here because I think it's going to be easier for you to understand how it works.
Once we have these two Read
instances, writin ghte one for Combin
is a straightforward matter of trying to read the whole string with reads
for Prefix
, and then trying to read each remainder as a Type
:
instance Read Combin where
readsPrec _ s = [(Combin pre typ, s'') | (pre, s') <- reads s, (typ, s'') <- reads s']
If you are familiar with do
notation and the []
monad, you can rewrite this as
instance Read Combin where
readsPrec _ s = do
(pre, s) <- reads s
(typ, s) <- reads s
return (Combin pre typ, s)
Upvotes: 2