在纯脚本中解码联合类型

Decoding Union types in purescript

我正在使用 purescript-agronauth 库将以下类型手动编码和解码为 json 并返回。但是下面的不行

data Attributes 
   = TextAlignment TextAlign
   | TextScale String
   | LineHeight String
instance encodeAttributes :: EncodeJson Attributes where
encodeJson r = 
    case r of
        (TextAlignment p) -> 
            "key" := (fromString "text-align")
            ~> "value" := p
        (TextScale p) -> 
            "key" := (fromString "font-size")
            ~> "value" := p
        (LineHeight p) -> 
            "key" := (fromString "line-height")
            ~> "value" := p
instance decodeElementAttributes :: DecodeJson ElementAttributes where
  decodeJson json = do
    obj <- decodeJson json
    key <- getField obj "key"
    value <- getField obj "value"
    case key of
        "text-align" -> Right $ TextAlignment value 
        "font-size" -> Right $ TextScale value
        "line-height" -> Right $ LineHeight value
        _ -> Left "Unkown element property"

data TextAlign
    = LeftAlign
    | RightAlign
    | CenterAlign
    | Justify
instance encodeTextAlign :: EncodeJson TextAlign where
    encodeJson r = 
        case r of
            LeftAlign -> fromString "left"
            RightAlign -> fromString "right"
            CenterAlign -> fromString "center"
            Justify -> fromString "justify"
instance decodeTextAlign :: DecodeJson TextAlign where
    decodeJson obj = do
       case toString obj of
         Just "left" -> Right LeftAlign
         Just "right" -> Right RightAlign
         Just "center" -> Right CenterAlign
         Just "justify" -> Right Justify
         Just _ -> Left "Unknown alignment"
         Nothing -> Left "Unknown alignment"

这给出了以下错误

  Could not match type

    TextAlign

  with type

    String


while checking that type t0
  is at least as general as type String
while checking that expression value
  has type String
in value declaration decodeElementAttributes

where t0 is an unknown type

基本上,我想知道在这种情况下解码 Sum 类型(如属性)的正确方法是什么

(..) But the following does not work

TLDR;这应该有效:

instance decodeElementAttributes :: DecodeJson Attributes where
  decodeJson json = do
    obj <- decodeJson json
    key <- getField obj "key"
    case key of
      "text-align" -> TextAlignment <$> getField obj "value"
      "font-size" -> TextScale <$> getField obj "value"
      "line-height" -> LineHeight <$> getField obj "value"
      _ -> Left "Unkown element property"

让我们试一下编译器并尝试推断 value 类型。 在 decodeJson 的 monadic 块中调用了 getField:

value <- getField obj "value"

getField 在其 return 类型上是多态的:

getField :: forall a. DecodeJson a => Object Json -> String -> Either String a

因此仅从这个调用我们无法猜测 value 的类型。我们需要更多信息/上下文。

但幸运的是,下面几行我们可以找到 value 的用法,这为我们提供了一些解决方案:

"text-align" -> Right $ TextAlignment value

所以可以肯定我们的 value 必须键入 TextAlign 因为 TextAlignment 构造函数需要这样的参数。

但是等等...下面一行还有 value 的另一种用法:

"font-size" -> Right $ TextScale value

这里我们遇到了一个问题,因为这告诉我们 value 的类型是 String 并且... TextAlign 同时... 我们别无选择,只能告诉世界我们的发现:

Could not match type

 TextAlign

with type

  String

Basically, I would like to know what would be the proper way to decode a Sum type like Attributes in this case

  • 你的方法我觉得可以。它使您可以完全控制编码/解码过程。虽然它可能容易出错...

  • 您可以尝试使用完全通用的解决方案,例如 purescript-argounaut-generic

  • 您也可以尝试不同的通用方法并使用 purescript-simple-json。我无法找到通用总和处理的示例 - 这里只是像编码/解码类型这样的枚举:https://www.reddit.com/r/purescript/comments/7b5y7q/some_extra_examples_of_simplejson_usage/。您可以随时向 Justin Woo 寻求建议 - 他是一位非常有求必应的作者:-)

  • 我还没有使用过 purescript-codec-argonaut 但它应该可以帮助您减少与编码和解码定义相关的一些重复。使用这种方法,我认为您仍然需要手动定义所有内容。

  • 这里很有趣 post,如果你在线路的两端(消费者和生产者)都没有 PureScript,我认为这是最相关的@garyb 关于通用编解码器的缺点: http://code.slipthrough.net/2018/03/13/thoughts-on-typeclass-codecs/

你的电线两端都有 PureScript 吗?