构建更高级的通用解析器

时间:2017-05-08 18:57:18

标签: haskell generic-programming

好的,这是我上一个问题(Generically) Build Parsers from custom data types?的延续。我接受了这个建议并决定使用generic-sop构建我的解析器,直到现在一切都很顺利。

我需要稍微扩展我的实现,以便处理更复杂的情况。即,请考虑这两个data定义,其中B建立在A之上:

data A = A String Int

data B = B A Double

为了通常解析所有data结构,我定义了以下class

class HasSimpleParser f where
  getSimpleParser :: Parser f

class Parsable f where
  getParser :: Parser f

class GenericallyParsable f where
  getGenericParser :: Parser f

Int, String, Double等原始类型可以很容易地成为HasSimpleParser的实例。然后,通过执行

,将A的数据结构设为Parsable的实例
instance (Generic r, Code r ~ '[xs], All HasSimpleParser xs) => Parsable r where
  getParser = to . SOP. Z <$> hsequence (hcpure (Proxy @HasSimpleParser) getSimpleParser)

我引入了类GenericallyParsable来解析像B这样的数据结构。所以我做了以下几点:

 instance (Generic r, Code r ~ '[xs], All Parsable xs) => GenericallyParsable r where
  getGenericParser = to . SOP. Z <$> hsequence (hcpure (Proxy @Parsable) getParser)

这个难题的最后几部分是解析功能:

parseA :: InputStream ByteString -> IO A
parseA = parseFromStream (getGenericParser @A)

parseB :: InputStream ByteString -> IO B
parseB = parseFromStream (getGenericParser @B)

但是,代码无法编译,我收到以下错误:

• Couldn't match type ‘'['[Char, [Char]]]’ with ‘'[]’
    arising from a use of ‘getGenericParser’
• In the first argument of ‘parseFromStream’, namely
    ‘(getGenericParser @A)’
  In the expression: parseFromStream (getGenericParser @A)
  In an equation for ‘parseA’:
      parseA = parseFromStream (getGenericParser @A)

那么我应该如何修改代码才能工作?

1 个答案:

答案 0 :(得分:2)

我认为GenericallyParsable类型类没有必要。

只需为HasSimpleParser上的A定义一个Parsable实例:

instance HasSimpleParser A where
    getSimpleParser = getParser 

如果您最终为记录声明了此类型的许多实例,则可以使用{-# language DefaultSignatures #-}并将HasSimpleParser的定义更改为

来简化该类型的实例
class HasSimpleParser c where
    getSimpleParser :: Parser c
    default getSimpleParser :: Parsable c => Parser c
    getSimpleParser = getParser

现在在记录实例中你只需要写:

instance HasSimpleParser A

事实上,即使是HasSimpleParserParsable之间的区别也许是不必要的。单个HasParser类型类包含基本类型和复合类型的实例就足够了。默认实现将使用generics-sop并且需要(Generic r, Code r ~ '[xs], All HasParser xs)约束。

class HasParser c where
    getParser :: Parser c
    default getParser :: (Generic c, Code c ~ '[xs], All HasParser xs) => Parser c
    getParser = ...generics-sop code here...