![](/img/trans.png)
[英]How to use mapReader from Control.Monad.Reader for reader monad?
[英]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
在幕后,您對getArguments
和produceResponse
定義本質上是:
getArguments :: Config -> Request -> Arguments
produceResponse :: Config -> Header -> Arguments -> Response
請注意,在這種情況下, Config
始終是第一個參數,因此getArguments req
類的東西將不起作用 - 畢竟, Request
是getArguments
的第二個參數,而不是第一個,因此您不能只將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
總是排在第一位。
像這樣定義getArguments
和produceResponse
在您的原始示例中效果很好:
args <- getArguments req
- getArguments
施加到req
產生一個Reader Config Arguments
和綁定到args
使得args
一個Arguments
produceResponse header args
- produceResponse
適用於header
和args
產生Reader Config Response
,這非常適合作為最后的陳述中do
塊請注意,我們可以對Reader
這樣的轉換,因為Reader
無論如何都只是一個函數,但一般來說,對於任何Monad m
, m (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.