I do not know a general solution. However, it is much easier to write Read instances by defining the readPrec method (especially Text.Read especially Text.ParserCombinators.ReadPrec for some additional functions and possibly Text.ParserCombinators.ReadP for even more) instead of using Parsec or the definition of readsPrec . This is not the fastest record you could write, but it should be fast enough.
import Text.Read instance Read Bar where readListPrec = readListPrecDefault readPrec = parens $ do Ident "Bar" <- lexP Ident td <- parens lexP case td of "TypeDecInt" -> Bar TypeDecInt <$> readPrec "TypeDecString" -> Bar TypeDecString <$> readPrec _ -> empty
If TypeDec more complex and you have Read instances for TypeDec Int and TypeDec String (using FlexibleInstances or a helper class), then you will likely need something more modular:
instance Read Bar where readListPrec = readListPrecDefault readPrec = parens $ do Ident "Bar" <- lexP (Bar <$> readPrec <*> (readPrec :: ReadPrec Int)) <|> (Bar <$> readPrec <*> (readPrec :: ReadPrec String))
Please note that in the second example, the GHC cannot independently determine what types should have alternatives, but when we fix the type of the second field, the output extends to the first field. Therefore, in the first embodiment, we will search only for "TypeDecInt" , and in the second we will search only for "TypeDecString" .
The dependent-sum package defines the type that your Bar generalizes.
data DSum tag f = forall a . !(tag a) :=> fa
It also defines a Read instance for DSum using some additional classes. The general approach looks solid, but I would recommend two changes. First, use readPrec instead of readPrec with lists. The second is to replace the type of a higher rank GReadResult following (much simpler) existential:
data GReadResult t = forall a . GReadResult (ta)
Of course, these changes will force you to implement it yourself, but this is good.