Мое использование текста Haskell Text.JSON считается уродливым? - PullRequest
5 голосов
/ 10 сентября 2010

То, что я пытаюсь сделать, это очень просто .

Я хотел бы преобразовать следующий JSON, который я получаю из внешнего источника:

[{"symbol": "sym1", "description": "desc1"}
 {"symbol": "sym1", "description": "desc1"}]

в следующие типы:

data Symbols = Symbols [Symbol]
type Symbol  = (String, String)

IВ итоге мы написали следующий код, используя Text.JSON:

instance JSON Symbols where
  readJSON (JSArray arr) = either Error (Ok . Symbols) $ resultToEither (f arr [])
    where
      f ((JSObject obj):vs) acc = either Error (\x -> f vs (x:acc)) $ resultToEither (g (fromJSObject obj) [])
      f [] acc                  = Ok $ reverse acc
      f _ acc                   = Error "Invalid symbol/description list"

      g ((name, JSString val):vs) acc = g vs ((name, fromJSString val):acc)
      g [] acc                        = valg acc
      g _ acc                         = Error "Invalid symbol/description record"

      valg xs = case (sym, desc) of
        (Nothing, _)            -> Error "Record is missing symbol"
        (_, Nothing)            -> Error "Record is missing description"
        (Just sym', Just desc') -> Ok (sym', desc')
        where
          sym = lookup "symbol" xs
          desc = lookup "description" xs

  showJSON (Symbols syms) = JSArray $ map f syms
    where
      f (sym, desc) = JSObject $ toJSObject [("symbol", JSString $ toJSString sym),
                                             ("description", JSString $ toJSString desc)]

Это стало самым неуклюжимым Haskell, который я когда-либо писал.readJSON просто не выглядит правильно.Конечно, showJSON существенно короче, но что случилось с этими JSString $ toJSString и JSObject $ toJSObject вещами, которые я вынужден вставить сюда?И resultToEither?

Я использую Text.JSON неправильно?Есть ли лучший способ?


Хорошо, это больше похоже на это.Я получил readJSON до следующего благодаря разъяснениям и идеям Романа и Грейзера.В каждой точке он обнаружит неправильно отформатированный JSON и выведет ошибку, а не выдаст исключение.

instance JSON Symbols where
  readJSON o = fmap Symbols (readJSON o >>= mapM f)
    where
      f (JSObject o) = (,) <$> valFromObj "symbol" o <*> valFromObj "description" o
      f _            = Error "Unable to read object"

Ответы [ 2 ]

6 голосов
/ 10 сентября 2010

Не могли бы вы изменить название на что-то более точное?От "Text.JSON в Haskell, который считается уродливым ..." до чего-то вроде "Мой код, использующий Text.JSON - как уродливый ..."

Половина вашего кода состоит из явной рекурсии - зачем вам это нужно?От быстрого взгляда должно хватить что-то вроде mapM.

Обновление: пример кода

instance JSON Symbols where
  readJSON (JSArray arr) = fmap Symbols (f arr)
  f = mapM (\(JSObject obj) -> g . fromJSObject $ obj)
  g = valg . map (\(name, JSString val) -> (name, fromJSString val))

  valg xs = case (sym, desc) of
    (Nothing, _)            -> Error "Record is missing symbol"
    (_, Nothing)            -> Error "Record is missing description"
    (Just sym', Just desc') -> Ok (sym', desc')
    where 
      sym = lookup "symbol" xs
      desc = lookup "description" xs
2 голосов
/ 10 сентября 2010

Немного переставил с красивого решения Романа. Я думаю, что это может быть немного более читабельным.

instance JSON Symbols where
  readJSON o = fmap Symbols (readJSON o >>= mapM f)
    where
      f (JSObject o) = let l = fromJSObject o
                       in do s <- jslookup "symbol" l
                             d <- jslookup "description" l
                             return (s,d)
      f _ = Error "Expected an Object"
      jslookup k l = maybe (Error $ "missing key : "++k) readJSON (lookup k l)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...