[英]Lifting a higher order function in Haskell
我正在嘗試構造一個類型的函數:
liftSumthing :: ((a -> m b) -> m b) -> (a -> t m b) -> t m b
其中t
是monad變壓器。 具體來說,我對此感興趣:
liftSumthingIO :: MonadIO m => ((a -> IO b) -> IO b) -> (a -> m b) -> m b
我擺弄了一些Haskell巫術庫,但無濟於事。 我該如何正確處理,或者可能是某個找不到的解決方案?
由於IO
類型處於負位置,因此無法在所有MonadIO
實例上通用完成此操作。 有一些針對特定實例( monad-control , monad-peel )的黑客程序庫,但是對於它們在語義上是否合理還存在一些爭議,特別是在它們如何處理異常和類似的奇怪IO
方面。
編輯:有些人似乎對正面/負面立場的區分感興趣。 實際上,沒什么好說的(您可能已經聽過,但是用另一個名字)。 該術語來自子類型世界。
子類型背后的直覺是“當a
可以在預期使用b
任何地方使用b
時, a
是b
的子類型(我將寫a <= b
”)。 在很多情況下,確定子類型很簡單。 對於產品,例如,每當a1 <= b1
和a2 <= b2
時(a1, a2) <= (b1, b2)
,這是非常簡單的規則。 但是有一些棘手的情況。 例如,我們何時應該確定a1 -> a2 <= b1 -> b2
?
好吧,我們有一個函數f :: a1 -> a2
和一個上下文,期望函數為b1 -> b2
類型。 因此上下文將使用f
的返回值,就好像它是b2
,因此我們必須要求a2 <= b2
。 棘手的是,上下文將為f
提供b1
,即使f
像使用a1
一樣使用它。 因此,我們必須要求b1 <= a1
-從您可能的猜測中回溯! 我們說a2
和b2
是“協變的”,或者出現在“正位置”,而a1
和b1
是“相反的”,或者出現在“負位置”。
(旁白:為什么“正”和“負”?它是由乘法驅動的。請考慮以下兩種類型:
f1 :: ((a1 -> b1) -> c1) -> (d1 -> e1)
f2 :: ((a2 -> b2) -> c2) -> (d2 -> e2)
f1
的類型何時應為f2
的類型的子類型? 我陳述這些事實(練習:使用上面的規則進行檢查):
e1 <= e2
。 d2 <= d1
。 c2 <= c1
。 b1 <= b2
。 a2 <= a1
。 e1
在d1 -> e1
中處於正位置,而在f1
類型中又處於正位置; 此外, e1
在總體f1
的類型中處於正位置(根據上述事實,由於它是協變的)。 它在整個術語中的位置是其在每個子術語中的位置的乘積:正*正=正。 類似地, d1
在d1 -> e1
中處於負位置,在整個類型中處於正位置。 負*正=負,並且d
變量確實是反變量。 b1
在類型a1 -> b1
中處於正位置,在(a1 -> b1) -> c1
中處於負位置,在整個類型中都處於負位置。 正*負*負=正,它是協變的。 你懂的。)
現在,讓我們看一下MonadIO
類:
class Monad m => MonadIO m where
liftIO :: IO a -> m a
我們可以將其視為子類型的明確聲明:對於特定的m
我們提供了一種使IO a
成為ma
的子類型的方法。 馬上我們知道我們可以將IO
構造函數置於正位置並將其變為m
s,從而獲得任何價值。 但這就是全部:我們無法將否定IO
構造函數轉換為m
s -為此,我們需要一個更有趣的類。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.