自定义Colour类型Read类实现问题:read无法解析自定义show输出
自定义Colour类型实现与单字母字符串互转
问题根源
你遇到的问题在于:派生的Read实例是和Haskell默认的Show行为绑定的——默认Show会输出构造器的完整名称(比如Red对应字符串"Red"),所以自动派生的Read只会识别这种格式的字符串。而你自定义了Show实例,让它返回单字母缩写("R"/"B"/"G"),两者格式不匹配,自然会触发解析失败的异常。
正确实现方式
要让read能识别单字母缩写,你需要手动实现Read实例,匹配自定义Show的输出格式。这里用readsPrec来实现最直观:
data Colour = Red | Blue | Green deriving (Eq) -- 自定义Show,输出单字母 instance Show Colour where show Red = "R" show Blue = "B" show Green = "G" -- 手动实现Read,解析单字母 instance Read Colour where -- readsPrec的第一个参数是解析优先级,枚举类型无需处理,直接忽略 readsPrec _ input = case input of 'R':rest -> [(Red, rest)] -- 匹配到'R',返回Red和剩余字符串 'B':rest -> [(Blue, rest)] 'G':rest -> [(Green, rest)] _ -> [] -- 不匹配任何情况,返回空列表表示解析失败
测试验证
在GHCi中测试:
> show Red "R" > read "R" :: Colour Red > read "B" :: Colour Blue > read "G" :: Colour Green
如果需要严格解析整个字符串(不允许有剩余字符),可以封装一个辅助函数:
textToData :: String -> Colour textToData s = case reads s of [(c, "")] -> c _ -> error $ "Invalid colour string: " ++ s
Read类工作原理
Read类的核心是readsPrec :: Int -> ReadS a函数:
- 第一个参数
Int是解析优先级,用于处理嵌套表达式(比如括号、运算符优先级),简单枚举类型可以直接忽略。 ReadS a是String -> [(a, String)]的别名,返回值是一个列表:每个元素是(解析出的值, 剩余未解析的字符串)。空列表表示解析失败。
read函数是基于readsPrec实现的,它会尝试解析整个字符串:如果reads返回的列表中存在一个元素,其剩余字符串为空,就返回对应的值;否则抛出解析异常。
自动派生的Read实例会生成与默认Show对应的解析逻辑——比如对于Red构造器,它会解析字符串"Red",而不是你自定义的"R",这就是为什么你之前用read "R"会失败。
内容的提问来源于stack exchange,提问作者Bassie-c
相关产品推荐
相关产品推荐

