使用 Haskell 读取组合数据类型的实例

Read instance of a combined datatype with Haskell

我在 Haskell 程序中实现了这些(简化的)数据类型:

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

通过这些实例,我能够 showread 类型 PrefixTypeCombin 数据类型是 PrefixType 的串联。 现在,我想为 Combin 数据类型实现读取实例,但我不知道该怎么做。

我考虑过推导Combin类型,但它导致Combin PrefA ValC的输出字符串为"Combin a mp"。这不是我想要的。我想 "amp" 连接在一起。读取相同的东西

我考虑过与输入字符串进行模式匹配,但 Prefix 字符串长度不同,可能无效 (NoPref)。

你有没有用 read 实现过这样的功能?你知道怎么做吗?

您的 readsPrec 实现是错误的,因为它们应该接受输入的有效前缀,而不一定会消耗整个输入。您的 readsPrec 函数不可组合。

解决方案的关键是重写它们,以便它们检查它们的输入是否与任何名称匹配:

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 = []

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)]

注意 readsPrec 的最后一个分支 Prefix,它表示每个不以 "a""bm""c" 开头的字符串将被解析为 NoPref。这仅适用于 Combin,因为没有 Type 以任何 Prefix 位开头;否则,Prefix 的解析器将需要是不确定的,因为像 "aXY" 这样的字符串可能对应于 "a"PrefixType "XY",或 NoPrefPrefix"aXY"Type。我在这里选择了确定性渴望的版本,因为我认为它会让您更容易理解它是如何工作的。

一旦我们有了这两个 Read 实例,为 Combin 写一个是一件简单的事情,尝试用 readsPrefix 读取整个字符串,然后尝试将每个余数读取为 Type:

instance Read Combin where
    readsPrec _ s = [(Combin pre typ, s'') | (pre, s') <- reads s, (typ, s'') <- reads s']

如果您熟悉 do 表示法和 [] monad,您可以将其重写为

instance Read Combin where
    readsPrec _ s = do
        (pre, s) <- reads s
        (typ, s) <- reads s
        return (Combin pre typ, s)