簡體   English   中英

對Haskell Monad變形金剛感到困惑

[英]Confused about Haskell Monad Transformers

我很困惑m應該放在Monad變形金剛的右側?

例如:

WriterT定義為

newtype WriterT w m a = WriterT { runWriterT :: m (a, w) }

ReaderT被定義為

newtype ReaderT r m a = ReaderT { runReaderT :: r -> m a }

但不是

newtype ReaderT r m a = ReaderT { runReaderT :: m (r -> a) }

monad m的放置將取決於應用於底層monad m的monad變換器的功能和操作,因此它取決於讀者和編寫者應該添加到monad的功能。

有助於記住runReaderTrunWriterT並沒有真正任何事情,盡管他們的名字是暗示性的。 他們只是打開一個新類型,而它們正在包裝的東西正在改變monad m

我的意思是,給定一個monad m ,你可以通過考慮類型的monadic動作添加一個讀者:

r -> m a

你可以通過考慮類型的monadic動作為它添加一個編寫器:

m (a, w)

並且您可以通過考慮類型的monadic動作為其添加讀者,編寫者和狀態:

r -> s -> m (a, s, w)

(也就是說,您不需要任何變換器包裝器來執行此操作,但它們可以使它更方便,特別是因為您可以使用現有的運算符,如>>=<*>而不必定義自己的運算符。)

那么,當你將一個閱讀器添加到monad m ,為什么不把m放在開頭並考慮下面類型的monadic動作?

m (r -> a)

事實上,您可以這樣做,但您很快就會發現這種添加閱讀器的方法實際上並沒有為monad m添加太多功能。

例如,假設您正在編寫一個應該在值表中查找鍵的函數,並且您希望在讀取器中攜帶該表。 由於查找失敗,您可以在Maybe monad中執行此操作。 所以,你想寫一些類似的東西:

myLookup :: Key -> Maybe Value
myLookup key = ...

但是,您希望使用提供鍵和值表的閱讀器來增強Maybe monad。 如果我們使用m (r -> a)模式,我們得到:

myLookup :: Key -> Maybe ([(Key,Value)] -> Value)

現在,讓我們嘗試實現它:

myLookup k = Just (\tbl -> ...)

我們已經看到了一個問題。 在允許編寫代碼來訪問\\tbl之前,我們必須提供一個Just (表示查找已成功)。 也就是說,monadic動作(失敗或返回值成功)不能依賴於r中的信息,這些信息應該從簽名m (r -> a)顯而易見。 使用備用r -> ma模式更強大:

type M a = ([Key,Value]) -> Maybe a
myLookup :: Key -> M Value
myLookup key tbl = Prelude.lookup key tbl

@Thomas_M_DuBuisson給出了另一個例子。 如果我們嘗試讀取輸入文件,我們可能會寫:

readInput :: FilePath -> IO DataToProcess
readInput fp = withFile fp ReadMode $ \h -> ...

在閱讀器中攜帶文件路徑等配置信息會很好,所以讓我們使用模式m (r -> a)將其轉換為:

data Config = Config { inputFile :: FilePath }
readConfig :: IO (Config -> DataToProcess)
readConfig = ...um...

我們因為無法編寫依賴於配置信息的IO操作而陷入困境。 如果我們使用備用模式r -> ma ,我們將設置:

type M a = Config -> IO a
readConfig :: M DataToProcess
readConfig cfg = withFile (inputFile cfg) ReadMode $ ...

@cdk提出的另一個問題是這個新的“monadic”動作類型:

m (r -> a)

甚至不是一個單子。 它更弱(只是一個應用程序)。

請注意,將單獨的應用程序讀取器添加到monad 仍然有用。 它只需要用於計算結構不依賴於r的信息的計算中。 (因此,如果底層monad Maybe允許計算發出錯誤信號,則r的值可用於計算,但確定計算是否成功必須獨立於r 。)

然而, r -> ma版本嚴格來說更強大,可以用作monadic和applicative閱讀器。

請注意,某些monadic轉換在多種形式中很有用。 例如,您可以(但有時只有@luqui在評論中指出)以兩種方式向m monad添加一個編寫器:

m (a, w)  -- if m is a monad this is always a monad
(m a, w)  -- this is a monad for some, but not all, monads m

如果mIO ,則IO (a,w)(IO a, w)更有用 - 對於后者,寫入的w (例如,錯誤日志)不能取決於執行IO的結果行動! 而且, (IO a, w)實際上並不是monad; 這只是一個應用。

另一方面,如果mMaybe ,則(Maybe a, w)寫入計算成功或失敗的內容,而Maybe (a, w)如果返回Nothing則丟失所有日志條目。 兩種形式都是monad,可以在不同情況下使用,它們對應於以不同順序堆疊變換器:

MaybeT (Writer w)  -- acts like  (Maybe a, w)
WriterT w Maybe    -- acts like  Maybe (a, w)

這同樣不是真正的堆疊MaybeReader以不同的順序。 這兩個都與“好”的讀者同構r -> Maybe a

MaybeT (Reader r)
ReaderT r Maybe

暫無
暫無

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

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