簡體   English   中英

在Haskell中提升高階函數

[英]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-controlmonad-peel )的黑客程序庫,但是對於它們在語義上是否合理還存在一些爭議,特別是在它們如何處理異常和類似的奇怪IO方面。

編輯:有些人似乎對正面/負面立場的區分感興趣。 實際上,沒什么好說的(您可能已經聽過,但是用另一個名字)。 該術語來自子類型世界。

子類型背后的直覺是“當a可以在預期使用b任何地方使用b時, ab的子類型(我將寫a <= b ”)。 在很多情況下,確定子類型很簡單。 對於產品,例如,每當a1 <= b1a2 <= b2(a1, a2) <= (b1, b2) ,這是非常簡單的規則。 但是有一些棘手的情況。 例如,我們何時應該確定a1 -> a2 <= b1 -> b2

好吧,我們有一個函數f :: a1 -> a2和一個上下文,期望函數為b1 -> b2類型。 因此上下文將使用f的返回值,就好像它是b2 ,因此我們必須要求a2 <= b2 棘手的是,上下文將為f提供b1 ,即使f像使用a1一樣使用它。 因此,我們必須要求b1 <= a1 -從您可能的猜測中回溯! 我們說a2b2是“協變的”,或者出現在“正位置”,而a1b1是“相反的”,或者出現在“負位置”。

(旁白:為什么“正”和“負”?它是由乘法驅動的。請考慮以下兩種類型:

f1 :: ((a1 -> b1) -> c1) -> (d1 -> e1)
f2 :: ((a2 -> b2) -> c2) -> (d2 -> e2)

f1的類型何時應為f2的類型的子類型? 我陳述這些事實(練習:使用上面的規則進行檢查):

  • 我們應該e1 <= e2
  • 我們應該讓d2 <= d1
  • 我們應該有c2 <= c1
  • 我們應該讓b1 <= b2
  • 我們應該有a2 <= a1

e1d1 -> e1中處於正位置,而在f1類型中又處於正位置; 此外, e1在總體f1的類型中處於正位置(根據上述事實,由於它是協變的)。 它在整個術語中的位置是其在每個子術語中的位置的乘積:正*正=正。 類似地, d1d1 -> 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.

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