我正在使用Haxl库,我正在尝试同时实现fetchHTML
:
import Data.Aeson
import Control.Concurrent.Async
import Control.Concurrent.QSem
import Haxl.Core
import Haxl.Prelude
instance DataSource' u HTTPRequest where
fetch = metaImplementation
data HTTPRequest a where
MakeRequest :: HTTPRequest Int
instance StateKey HTTPRequest where --Link HTTPRequest to State class
data State HTTPRequest =
HTTPRequestState {threadNum :: Int}
initialiseState :: Int -> IO (State HTTPRequest)
initialiseState threads = do
return HTTPRequestState {threadNum = threads}
metaImplementation :: State HTTPRequest -> Flags -> u -> [BlockedFetch' HTTPRequest] -> PerformFetch
metaImplementation HTTPRequestState{..} _flags user bfs =
AsyncFetch $ \inner -> do
sem <- newQSem threadNum
asyncs <- mapM (implementation sem) bfs
inner
mapM_ wait asyncs
implementation :: QSem -> BlockedFetch' HTTPRequest -> IO(Async())
implementation sem (BlockedFetch' request returnVal) =
async $ bracket_ (waitQSem sem) (signalQSem sem) $ do
e <- Control.Exception.try $
fetchHTML
case e of
Left ex -> putFailure returnVal (ex :: SomeException)
Right el -> putSuccess returnVal el
fetchHTML :: IO Int
fetchHTML = do
res <- get "https://example.com"
let resBody = res ^. responseBody
return (200)
makeHTTPRequest :: GenHaxl u Int --Perform concurrent fetches
makeHTTPRequest = dataFetch (MakeRequest)
我面临的问题是Haxl的BlockedFetch
是多态的:
BlockedFetch :: forall (r :: * -> *) a. r a -> ResultVar a -> BlockedFetch r
但我希望fetchHTML
是单态的(只返回一个Int):
fetchHTML :: IO Int
fetchHTML = do
res <- get "https://www.bbc.com"
let resBody = res ^. responseBody
return (200)
因此,当我尝试编译时出现以下错误:
Couldn't match expected type ‘a’ with actual type ‘Int’
‘a’ is a rigid type variable bound by
a pattern with constructor:
BlockedFetch :: forall (r :: * -> *) a.
r a -> ResultVar a -> BlockedFetch r,
in an equation for ‘implementation’
最初我认为我可以重新定义BlockedFetch
:
data BlockedFetch' a where --Custom monomorphic implementation of BlockedFetch
BlockedFetch' :: HTTPRequest Int -> ResultVar Int -> BlockedFetch' HTTPRequest
然而,这需要DataSource
的新实施,以使其能够接收我的自定义BlockFetch'
:
class (DataSourceName r, StateKey r) => DataSource' u r where
fetch :: State r -> Flags -> u -> [BlockedFetch' r] -> PerformFetch
显然,这只会影响倒退并要求我重新编写整个Haxl模块!
我的问题是:
1)是否有一种简单的方法可以使fetchHTML
多态? (我不太关心它返回什么,只是它在完成后返回某些东西)
2)遇到这类问题时,Haskell程序员的一般方法是什么?
答案 0 :(得分:3)
The BlockedFetch
constructor存在量化a
:
data BlockedFetch r = forall a. BlockedFetch (r a) (ResultVar a)
这意味着创建BlockedFetch
的人可以选择a
是什么,但在解压缩BlockedFetch
a
时会保持抽象,并且不会与别的什么。
但是,您可以访问r
类型。通过选择r
作为GADT,您可以将a
约束为(一组)特定类型之一,并通过匹配GADT的构造函数来恢复该信息。您不必重写任何Haxl代码 - 它旨在允许您插入自己的r
!
在这种情况下,我发现你已经有90%的路在那里:
data HttpRequest a where
MakeRequest :: HttpRequest Int
因此,当您在MakeRequest
构造函数上匹配时,您将获得a ~ Int
的知识。
implementation :: QSem -> BlockedFetch' HTTPRequest -> IO(Async())
-- match the MakeRequest constructor
implementation sem (BlockedFetch' MakeRequest returnVal) =
-- as before