使用具有多态Haxl库的单态函数?

时间:2017-01-22 16:38:22

标签: haskell polymorphism monomorphism haxl

我正在使用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程序员的一般方法是什么?

1 个答案:

答案 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