Как парсить JSON с помощью Aeson без объявления отдельных типов

(Предупреждение для новичков в Haskell)

Вот фрагмент кода, с которым я борюсь. По сути, я получаю JSON, поступающий из веб-сокета, и хочу проанализировать его с помощью Aeson, не определяя отдельные типы данных для каждого ответа.

import Data.Aeson
import qualified Network.WebSockets  as WS

aria2WebsocketReceiver :: WS.Connection -> IO ()
aria2WebsocketReceiver conn = do
  msg <- WS.receiveData conn
  let res = decode msg
  let v = flip parseMaybe res $ \o -> do
                                      r <- o .: "result"
                                      version <- r .: "version"
                                      enabledFeatures <- r .: "enabledFeatures"
                                      id_ <- r .: "id"
                                      return $ "version=" ++ version
  putStrLn (show v)
  aria2WebsocketReceiver conn

Вот ошибки компиляции, с которыми я сталкиваюсь:

Nightwatch/Telegram.hs:244:13:
    No instance for (FromJSON a0) arising from a use of ‘decode’
    The type variable ‘a0’ is ambiguous
    Relevant bindings include
      res :: Maybe a0 (bound at Nightwatch/Telegram.hs:244:7)
    Note: there are several potential instances:
      instance FromJSON Chat -- Defined at Nightwatch/Telegram.hs:90:10
      instance FromJSON Message
        -- Defined at Nightwatch/Telegram.hs:106:10
      instance FromJSON TelegramResponse
        -- Defined at Nightwatch/Telegram.hs:122:10
      ...plus two others
    In the expression: decode msg
    In an equation for ‘res’: res = decode msg
    In the expression:
      do { msg <- WS.receiveData conn;
           let res = decode msg;
           let v = flip parseMaybe res $ ...;
           putStrLn (show v);
           .... }

Nightwatch/Telegram.hs:246:44:
    Couldn't match type ‘Maybe a0’
                   with ‘unordered-containers-0.2.5.1:Data.HashMap.Base.HashMap
                           Text Value’
    Expected type: Object
      Actual type: Maybe a0
    Relevant bindings include
      o :: Maybe a0 (bound at Nightwatch/Telegram.hs:245:34)
      res :: Maybe a0 (bound at Nightwatch/Telegram.hs:244:7)
    In the first argument of ‘(.:)’, namely ‘o’
    In a stmt of a 'do' block: r <- o .: "result"

В основном я пытаюсь воспроизвести пример «Работа с AST», приведенный на https://hackage.haskell.org/package/aeson-0.10.0.0/docs/Data-Aeson.html


person Saurabh Nanda    schedule 29.01.2016    source источник


Ответы (1)


Спасибо Кейлу из #haskell, вот рабочий код:

aria2WebsocketReceiver :: WS.Connection -> IO ()
aria2WebsocketReceiver conn = do
  msg <- WS.receiveData conn
  let v = do res <- decode msg
             flip parseMaybe res $ \o -> do
                                         r <- o .: "result"
                                         version <- r .: "version"
                                         return $ "version=" ++ (version :: String)
  putStrLn (show v)
  aria2WebsocketReceiver conn

В предыдущем коде было три проблемы:

  1. Тип decode msg — это Maybe, который должен находиться внутри отдельного блока do.
  2. Из-за {-# LANGUAGE OverloadedStrings #-} компилятор не смог вывести тип для version, отсюда и подсказка (version :: String).
  3. Точно так же enabledFeatures и id_ присваивались, но нигде не использовались, что вызывало дополнительные проблемы при выводе типов.
person Saurabh Nanda    schedule 29.01.2016
comment
Что касается 3, это типичная проблема с Haskell (GHC) и особенно с библиотеками с предоставлением DSL - они полагаются на вывод типа для работы, а вывод типа работает только в том случае, если вы действительно используете тип значение где-то! Это, очевидно, хорошо на практике, но при тестировании и экспериментировании вы часто будете сталкиваться с этой ситуацией. Вообще говоря, ошибка неоднозначного типа соответствует чему-то вроде этого (неиспользованное полиморфное значение) - если вы видите эту ошибку, ищите подобные вещи. - person user2407038; 29.01.2016