我正在閱讀Monads之書,并使用以下示例介紹了使用Reader動機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檔案。
uj5u.com熱心網友回復:
這種嘗試絕對不幼稚,盡管這不是通常的做事方式。
由于Reader r a實際上只是r -> a幕后的一個函式,因此您對getArguments和的定義produceResponse本質上是:
getArguments :: Config -> Request -> Arguments
produceResponse :: Config -> Header -> Arguments -> Response
請注意,在這種情況下,Config始終是第一個引數,所以像getArguments req將無法正常作業-畢竟,Request是第二個引數getArguments,而不是第一個,所以你不能只適用getArguments于req。
你需要做的是先申請getArguments到Config。由于我們實際上使用的是 aReader Config ...而不是簡單的函式Config -> ...,因此我們通過系結來實作getArguments:
handle req = do header <- initializeHeader
argumentGetter <- getArguments
-- rest omitted
由于getArgumentsis a 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,事情就會奏效。
然而,一開始我說這不是通常的做事方式。考慮這些定義:
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一個ArgumentsproduceResponse header args-produceResponse應用于header并args產生 aReader Config Response,它非常適合作為do塊中的最后一條陳述句
請注意,我們可以進行這樣的轉換,Reader因為因為Reader無論如何都只是一個函式,但總的來說,對于 any Monad m,m (x -> y)and之間存在相當多的差異x -> m y,后者是“引數化” monadic 操作中最常見的。
例如,考慮readFile :: FilePath -> IO String. 您提供 a FilePath,它為您提供生成檔案內容的 IO 操作。相反,如果是IO (FilePath -> String),它將是一個 IO 操作,它產生一個函式FilePath -> String——也就是說,一個純函式,當給定 a 時FilePath,它會產生一個檔案的內容。然而,由于它是一個純函式,它不會有任何副作用,所以它實際上不能讀取檔案,因此這不會真正起作用(getFile可以先做一些瘋狂的事情,比如先讀取整個檔案系統,但我們不要進入那個)。
uj5u.com熱心網友回復:
我過去使用的一種選擇是制作自定義記錄型別
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
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/352617.html
