0

我正在使用 GHCJSi,版本 0.2.0-7.10.3:http ://www.github.com/ghcjs/ghcjs/ 和来自https://github.com/reflex-的 reflex-dom 库版本 0-4 frp/反射域。我没有使用来自 Hackage 的 reflex-dom-0.3。

以下 Haskell 程序无法使用 reflex-dom-0.4 编译:

{-# LANGUAGE RecursiveDo, ScopedTypeVariables, DeriveGeneric, OverloadedStrings #-}
import Reflex
import Reflex.Dom
import Data.Aeson
import GHC.Generics
import qualified Data.Text as T
data Apod = Apod { copyright :: T.Text
                 , date :: T.Text
                 , explanation :: T.Text
                 , hdurl :: T.Text
                 , media_type :: T.Text
                 , service_version :: T.Text
                 , title :: T.Text
                 , url :: T.Text
                 } deriving (Generic, Show)
instance FromJSON Apod
main :: IO ()
main = do
  mainWidget $ el "div" $ do
    buttonEvent <- button "GET"
    let url = "https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY"
    let defaultReq = xhrRequest "GET" url def  
    asyncEvent <- performRequestAsync (tag (constant defaultReq) buttonEvent)
    let rspApod = fmapMaybe (\r -> decodeXhrResponse r) asyncEvent
    return ()

我得到错误

 Xhr00.hs:24:36:
    No instance for (aeson-0.9.0.1:Data.Aeson.Types.Class.FromJSON b0)
      arising from a use of ‘decodeXhrResponse’
    The type variable ‘b0’ is ambiguous
    Relevant bindings include
      rspApod :: Event Spider b0 (bound at Xhr00.hs:24:9)
    Note: there is a potential instance available:
      instance (aeson-0.9.0.1:Data.Aeson.Types.Class.FromJSON a,
                aeson-0.9.0.1:Data.Aeson.Types.Class.FromJSON b) =>
                aeson-0.9.0.1:Data.Aeson.Types.Class.FromJSON
                 (Data.These.These a b)
        -- Defined in ‘Data.These’
       In the expression: decodeXhrResponse r
    In the first argument of ‘fmapMaybe’, namely
      ‘(\ r -> decodeXhrResponse r)’
    In the expression:
      fmapMaybe (\ r -> decodeXhrResponse r) asyncEvent
Failed, modules loaded: none.

我内联了 reflex-dom 库函数decodeXhrResponse(以及decodeText)。我将类型签名更改为FromJSON a => XhrResponse -> Maybe a没有类型变量的签名XhrResponse -> Maybe Apod。然后程序编译成功。

{-# LANGUAGE RecursiveDo, ScopedTypeVariables, DeriveGeneric, OverloadedStrings #-}
import Reflex
import Reflex.Dom hiding (decodeXhrResponse, decodeText)
-- import Reflex.Dom
import Data.Aeson
import GHC.Generics
import qualified Data.Text as T
import Control.Monad
import qualified Data.ByteString.Lazy as BL
import Data.Text.Encoding
data Apod = Apod { copyright :: T.Text
                 , date :: T.Text
                 , explanation :: T.Text
                 , hdurl :: T.Text
                 , media_type :: T.Text
                 , service_version :: T.Text
                 , title :: T.Text
                 , url :: T.Text
                 } deriving (Generic, Show)
instance FromJSON Apod
main :: IO ()
main = do
  mainWidget $ el "div" $ do
    buttonEvent <- button "GET"
    let nasa = "https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY"
    let defaultReq = xhrRequest "GET" nasa def 
    asyncEvent <- performRequestAsync (tag (constant defaultReq) buttonEvent)
    let rspApod :: Event Spider Apod = fmapMaybe (\r -> decodeXhrResponse r) asyncEvent
    return ()
-- Inlined and changed library function:
-- decodeXhrResponse :: FromJSON a => XhrResponse -> Maybe a
decodeXhrResponse :: XhrResponse -> Maybe Apod
decodeXhrResponse = join . fmap decodeText . _xhrResponse_responseText
-- Inlined and changed library function:
-- decodeText :: FromJSON a => T.Text -> Maybe a
decodeText :: T.Text -> Maybe Apod
decodeText = decode . BL.fromStrict . encodeUtf8

我试图为 rspApod 添加一个作用域类型变量,例如 rspApod :: Event t Apodor rspApod :: Event Spider Apod但它没有帮助。

问题:

如何更改第一个程序才能成功编译?(内联和更改库函数是一个非常糟糕的技巧!)

为什么编译器没有找到并使用FromJSON数据类型的实例Apod

4

1 回答 1

3

所以函数的原始签名是

decodeXhrResponse :: FromJSON a => XhrResponse -> Maybe a

因此,在使用时,编译器需要FromJSON为给定的a. 在你的情况下aApod,所以编译器应该罚款的FromJSON实例Apod。在您的代码中,编译器无法知道这是您的意图。这是解析时的常见问题,需要告诉编译器目标类型应该是什么。

现在你可能会争辩说它应该能够通过周围的代码来确定目标类型,比如asyncEvent,但它不是出于某种原因。可能是周围的代码同样通用。考虑以下场景:

main = print $ read x

编译器如何知道读取的目标类型x

read :: Read a => String -> a显然这并没有通知它目标。

print :: Show a => a -> IO ()这只是断言a必须有一个Show实例。

a太通用而无法解析,我们需要一个具体的类型。

因此,当内联函数并更改类型签名以包含Apod时,您向编译器提供了它需要知道FromJSON要查找哪个实例的信息。

以下是我将如何解决这个问题:

main :: IO ()
main = do
  mainWidget $ el "div" $ do
    buttonEvent <- button "GET"
    let url = "https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY"
    let defaultReq = xhrRequest "GET" url def  
    asyncEvent <- performRequestAsync (tag (constant defaultReq) buttonEvent)
    let rspApod = fmapMaybe (\r -> decodeXhrResponse r :: Maybe Apod) asyncEvent
    return ()

添加:: Maybe Apod内联类型注释应该为编译器提供了解您预期的解析目标所需的信息。以这种方式使用类型签名是合理的,因为它实际上是有效的。

希望有帮助!

于 2016-12-29T04:04:59.290 回答