Haskell:如何修复 "type variable ambigous" 编译器错误?
Haskell: How to fix the "type variable ambigous" compiler error?
我正在使用 GHCJSi,版本 0.2.0-7.10.3:http://www.github.com/ghcjs/ghcjs/ and the reflex-dom library version 0-4 from https://github.com/reflex-frp/reflex-dom。我没有使用 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 Apod
或 rspApod :: Event Spider Apod
,但没有帮助。
问题:
第一个程序怎么改才能编译成功? (内联和更改库函数是一个非常糟糕的 hack!)
为什么编译器找不到和使用数据类型 Apod
的 FromJSON
实例?
所以函数的原始签名是
decodeXhrResponse :: FromJSON a => XhrResponse -> Maybe a
因此,在使用时,编译器需要为给定的 a
找到 FromJSON
实例。在您的情况下,a
是 Apod
,因此编译器应该为 Apod
调整 FromJSON
实例。在您的代码中,编译器无法知道您的意图。这是解析时的一个常见问题,编译器需要被告知目标类型应该是什么。
现在您可能会争辩说它应该能够通过周围的代码确定目标类型,例如 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
内联类型注释应该为编译器提供了解您的预期解析目标所需的信息。这样使用类型签名是合理的,因为它确实有效。
希望对您有所帮助!
我正在使用 GHCJSi,版本 0.2.0-7.10.3:http://www.github.com/ghcjs/ghcjs/ and the reflex-dom library version 0-4 from https://github.com/reflex-frp/reflex-dom。我没有使用 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 Apod
或 rspApod :: Event Spider Apod
,但没有帮助。
问题:
第一个程序怎么改才能编译成功? (内联和更改库函数是一个非常糟糕的 hack!)
为什么编译器找不到和使用数据类型 Apod
的 FromJSON
实例?
所以函数的原始签名是
decodeXhrResponse :: FromJSON a => XhrResponse -> Maybe a
因此,在使用时,编译器需要为给定的 a
找到 FromJSON
实例。在您的情况下,a
是 Apod
,因此编译器应该为 Apod
调整 FromJSON
实例。在您的代码中,编译器无法知道您的意图。这是解析时的一个常见问题,编译器需要被告知目标类型应该是什么。
现在您可能会争辩说它应该能够通过周围的代码确定目标类型,例如 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
内联类型注释应该为编译器提供了解您的预期解析目标所需的信息。这样使用类型签名是合理的,因为它确实有效。
希望对您有所帮助!