Haskell:如何解决“类型变量ambigous”编译器错误?
我使用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 Apod
或rspApod :: Event Spider Apod
,但它并没有帮助。
问题:
我该如何改变第一个程序才能成功编译? (内联和更改库函数是一个非常糟糕的破解!)
为什么编译器没有找到并使用FromJSON
实例作为数据类型Apod
?
因此函数的原始签名
decodeXhrResponse :: FromJSON a => XhrResponse -> Maybe a
需要找到FromJSON
实例给定a
。在你的情况下,a
是Apod
,所以编译器应该罚款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
内嵌式注释应该给编译器,它需要知道你的意图解析的目标的信息。以这种方式使用类型签名是合理的,因为它实际上是有效的。
希望有帮助!
非常感谢您的善意解释和解决方案。您的解决方案与Reid的解决方案相同。不幸的是,它并没有帮助 – Jogger
即使我注释了两边'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
我添加了一个额外的实例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
'(\ r - > decodeXhrResponse r)'〜>'decodeXhrResponse' –
您的原始程序不明确。如何添加一个类型签名'decodeXhrResponse r :: Maybe Apod'。 –
@Reid:谢谢,但改成'let rspApod = fmapMaybe(\ r - > decodeXhrResponse r :: Maybe Apod)asyncEvent'不能解决问题:给出同样的'No instance for'错误。不幸的是... – Jogger