簡體   English   中英

如何將附加參數傳遞給 Control.Monad.Reader 實例

[英]How to pass additional arguments to a Control.Monad.Reader instance

我正在閱讀Monads書,然后進入了Reader monad,其中使用以下示例展示了使用Reader的動機:

handle :: Config -> Request -> Response
handle cfg req =
    produceResponse cfg (initializeHeader cfg) (getArguments cfg req)

為了避免顯式傳遞cfg參數, Reader使用如下:

handle :: Request -> Reader Config Response
handle req = do header <- initializeHeader
                args <- getArguments req
                produceResponse header args

讓我感到困惑的部分是cfg旁邊的函數采用附加參數。 我怎樣才能做到這一點?

在一個非常天真的嘗試中,我嘗試了這個:

getArguments :: Reader Config (Request -> Arguments)
produceResponse :: Reader Config (Header -> Arguments -> Response)

我還搜索了幾本書和Reader文檔。

這種嘗試絕對不幼稚,盡管這不是通常的做事方式。

由於Reader ra實際上只是一個函數r -> a在幕后,您對getArgumentsproduceResponse定義本質上是:

getArguments :: Config -> Request -> Arguments
produceResponse :: Config -> Header -> Arguments -> Response

請注意,在這種情況下, Config始終是第一個參數,因此getArguments req類的東西將不起作用 - 畢竟, RequestgetArguments的第二個參數,而不是第一個,因此您不能只將getArguments應用於req

您需要做的是首先將getArguments應用於Config 由於我們實際上是在使用Reader Config ...而不是簡單的函數Config -> ... ,我們通過綁定getArguments做到這getArguments

handle req = do header <- initializeHeader
                argumentGetter <- getArguments
                -- rest omitted

由於getArguments是一個Reader Config (Request -> Arguments)argumentGetter將只是一個函數Request -> Arguments 使用這樣的函數是微不足道的:

handle :: Request -> Reader Config Response
handle req = do header <- initializeHeader
                argumentGetter <- getArguments
                let args = argumentGetter req
                -- rest omitted

然后,您可以繼續對生產produceResponse應用相同的處理produceResponse ,事情就會奏效。

然而,一開始我說這不是通常的做事方式。 考慮這些定義:

getArguments :: Request -> Reader Config Arguments
produceResponse :: Header -> Arguments -> Reader Config Response

如果我們打開Reader ,我們會看到這些實際上只是:

getArguments :: Request -> Config -> Arguments
produceResponse :: Header -> Arguments -> Config -> Response

也就是說, Config總是作為最后一個參數出現。 這與我們一開始所擁有的一致,其中Config總是排在第一位。

像這樣定義getArgumentsproduceResponse在您的原始示例中效果很好:

  • args <- getArguments req - getArguments施加到req產生一個Reader Config Arguments和綁定到args使得args一個Arguments
  • produceResponse header args - produceResponse適用於headerargs產生Reader Config Response ,這非常適合作為最后的陳述中do

請注意,我們可以對Reader這樣的轉換,因為Reader無論如何都只是一個函數,但一般來說,對於任何Monad mm (x -> y)x -> my之間存在相當大的差異,其中后者是“參數化” monadic 操作中最常見的。

例如,考慮readFile :: FilePath -> IO String 您提供了一個FilePath ,它為您提供了一個生成文件內容的 IO 操作。 相反,如果它是IO (FilePath -> String) ,它將是一個 IO 操作,它產生一個函數FilePath -> String - 也就是說,一個純函數,當給定FilePath ,會產生文件的內容。 然而,由於它是一個純函數,它不會有任何副作用,所以它實際上無法讀取文件,因此這不會真正起作用( getFile可以做一些瘋狂的事情,比如先讀取整個文件系統,但我們不要進入那)。

我過去使用的一種選擇是制作自定義記錄類型

data Context = Context
   { config :: Config
   , header :: Header
   , args :: Arguments
   }

然后,您可以像這樣使用它:

foo :: Int -> Reader Context String
foo x = do
   h <- asks header        -- get the header from the implicit context
   a <- asks args
   doSomething x h a

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM