首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >解析json时构造GADT

解析json时构造GADT
EN

Stack Overflow用户
提问于 2020-07-15 13:07:28
回答 1查看 178关注 0票数 2

我有一个用GADT创建的数据结构,我想使用aeson解析一些json到这个GADT。但是类型检查程序抱怨说,在所有情况下都只能创建GADT的一个构造函数。参见此示例:

代码语言:javascript
复制
data Foo = Hello | World

data SFoo :: Foo -> Type where
  SHello :: SFoo 'Hello 
  SWorld :: SFoo 'World

instance FromJSON (SFoo a) where
  parseJSON = withText "Foo" \case
    "hello" -> pure SHello
    "world" -> pure SWorld

因此,我希望能够将"hello“字符串解析为SHello,将"world”字符串解析为SWorld。类型检查器会出现以下错误:

代码语言:javascript
复制
• Couldn't match type ‘'World’ with ‘'Hello’
  Expected type: Parser (SFoo 'Hello)
    Actual type: Parser (SFoo 'World)
• In the expression: pure SWorld
  In a case alternative: "world" -> pure SWorld
  In the second argument of ‘withText’, namely
    ‘\case
       "hello" -> pure SHello
       "world" -> pure SWorld’

如何将json解析为这样的GADT结构?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-07-15 13:46:36

代码语言:javascript
复制
instance FromJSON (SFoo a) where

不会飞。你会得到

代码语言:javascript
复制
parseJSON :: forall a. Value -> Parser (SFoo a)

这意味着调用方可以选择他们想要的a,而parseJSON并不控制从JSON解析a。相反,你想

代码语言:javascript
复制
data SomeFoo = forall a. SomeFoo (SFoo a)
instance FromJSON SomeFoo where
    parseJSON = withText "Foo" \case
        "hello" -> pure $ SomeFoo SHello
        "world" -> pure $ SomeFoo SWorld
        _ -> fail "not a Foo" -- aeson note: without this you get crashes!

现在在哪里

代码语言:javascript
复制
fromJSON :: Value -> Result SomeFoo

不告诉您它将以其类型返回SFoo的哪个分支。SomeFoo现在是一对a :: Foo类型和SFoo a值。fromJSON现在负责解析整个对,因此它控制返回的类型和值。当您使用它并在SomeFoo上匹配时,这将告诉您必须处理哪种类型:

代码语言:javascript
复制
example :: Value -> IO ()
example x = case fromJSON x of
    Error _ -> return ()
    Success (SomeFoo x) -> -- know x :: SFoo a where a is a type extracted from the match; don't know anything about a yet
        case x of
            SHello -> {- now know a ~ Hello -} return ()
            SWorld -> {- now know a ~ World -} return ()

注意,SomeFoo基本上是与Foo同构的。你还是写信吧

代码语言:javascript
复制
instance FromJSON Foo where ..

然后

代码语言:javascript
复制
someFoo :: Foo -> SomeFoo
someFoo Hello = SomeFoo SHello
someFoo World = SomeFoo SWorld
instance FromJSON SomeFoo where parseJSON = fmap someFoo . parseJSON

请注意,您可以编写以下两个实例:

代码语言:javascript
复制
instance FromJSON (SFoo Hello) where
    parseJSON = withText "SFoo Hello" \case
        "hello" -> pure SHello
        _ -> fail "not an SFoo Hello"
instance FromJSON (SFoo World) where
    parseJSON = withText "SFoo World" \case
        "world" -> pure SWorld
        _ -> fail "not an SFoo World"

...but --它们不是特别有用,只是作为另一种方式编写FromJSON SomeFoo

代码语言:javascript
复制
instance FromJSON SomeFoo where
    parseJSON x = prependFailure "SomeFoo: " $
        SomeFoo @Hello <$> parseJSON x <|> SomeFoo @World <$> parseJSON x
票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/62915610

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档