2016-12-28 68 views
0

我使用GHCJSi,版本0.2.0-7.10.3:http://www.github.com/ghcjs/ghcjs/https://github.com/reflex-frp/reflex-dom的反射DOM库0-4。我没有使用Hackage的reflex-dom-0.3。Haskell:如何解决“类型变量ambigous”编译器错误?

下面的Haskell程序不与反射-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. 

我内联反射-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 ApodrspApod :: Event Spider Apod,但它并没有帮助。

问题:

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

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

+0

'(\ r - > decodeXhrResponse r)'〜>'decodeXhrResponse' –

+0

您的原始程序不明确。如何添加一个类型签名'decodeXhrResponse r :: Maybe Apod'。 –

+0

@Reid:谢谢,但改成'let rspApod = fmapMaybe(\ r - > decodeXhrResponse r :: Maybe Apod)asyncEvent'不能解决问题:给出同样的'No instance for'错误。不幸的是... – Jogger

回答

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内嵌式注释应该给编译器,它需要知道你的意图解析的目标的信息。以这种方式使用类型签名是合理的,因为它实际上是有效的。

希望有帮助!

+0

非常感谢您的善意解释和解决方案。您的解决方案与Reid的解决方案相同。不幸的是,它并没有帮助 – Jogger

+0

即使我注释了两边'let rspApod :: Event Spider(Maybe Apod)= fmap(\ r - > decodeXhrResponse r :: Maybe Apod)asyncEvent',我仍然得到错误“No ('aeson-0.9.0.1:Data.Aeson.Types.Class.FromJSON Apod)由于使用了'decodeXhrResponse'而产生的实例 – Jogger

+0

我添加了一个额外的实例FromJSON(也许是Apod)声明,我得到了2个错误:'Overlapping使用'aeson-1.0.2.1:Data.Aeson.Types.FromJSON。$ gdmparseJSON''和'没有实例(aeson-0.9.0.1:Data.Aeson.Types)的FromJSON(可能是Apod) 的实例。 Class.FromJSON Apod)'。看来,有2个不同版本的Data.Aeson 1.0和0.9!这怎么可能 ?? – Jogger