1

我正在使用 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)

我面临的问题是 HaxlBlockedFetch是多态的:

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 程序员遇到此类问题时的一般方法是什么?

4

1 回答 1

3

构造BlockedFetch函数存在性地量化a

data BlockedFetch r = forall a. BlockedFetch (r a) (ResultVar a)

这意味着无论谁创建 aBlockedFetch都可以选择是什么a,但在打开包装时 aBlockedFetch 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
于 2017-01-22T19:05:45.797 回答