Расшифровка объединений типов в purescript - PullRequest
0 голосов
/ 11 ноября 2018

Я использую библиотеку 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

В принципе, я хотел бы знать, каков будет правильный способ декодировать тип суммы, такой как атрибуты, в этом случае

1 Ответ

0 голосов
/ 12 ноября 2018

(..) Но следующее не работает

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 происходит вызов getField:

value <- getField obj "value"

getField полиморфен по своему типу возврата:

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

В принципе, я хотел бы знать, каков будет правильный способ декодировать тип суммы, такой как атрибуты, в этом случае

  • Ваш подход мне подходит. Это дает вам полный контроль над процессом кодирования / декодирования. Это может быть подвержено ошибкам, хотя ...

  • Вы можете попробовать и использовать полностью универсальное решение, например purescript-argounaut-generic.

  • Вы также можете попробовать другой общий подход и использовать purescript-simple-json. Я не смог найти пример для обработки общих сумм - здесь только enum как тип, закодированный / декодированный: https://www.reddit.com/r/purescript/comments/7b5y7q/some_extra_examples_of_simplejson_usage/. Вы всегда можете спросить у Джастина Ву предложения - он действительно отзывчивый автор: -)

  • Я еще не использовал purescript-codec-argonaut, но это должно помочь вам минимизировать дублирование, связанное с определениями кодирования и декодирования. При таком подходе вы по-прежнему отвечаете за определение всего вручную, я думаю.

  • Вот интересный пост, который я считаю наиболее актуальным, если у вас нет PureScript на обоих концах провода (потребитель и производитель) @garyb о недостатках родовых кодеков: http://code.slipthrough.net/2018/03/13/thoughts-on-typeclass-codecs/

У вас есть PureScript на обоих концах провода?

...