2012-02-11 96 views
13

我試圖構造類型的函數:提升高階函數在Haskell

liftSumthing :: ((a -> m b) -> m b) -> (a -> t m b) -> t m b 

其中t是一個單子轉換。具體而言,我有興趣這樣做:

liftSumthingIO :: MonadIO m => ((a -> IO b) -> IO b) -> (a -> m b) -> m b 

我擺弄了一些哈斯克爾魔法庫,但無濟於事。我如何獲得它 是正確的,或者有一個我找不到的現成解決方案?

+1

嘎!爲什麼所有的Haskell問題都這麼難?這裏沒有簡單的一點:-( – drozzy 2012-02-11 20:35:12

+3

@drozzy:這個標籤實際上[每個答案的平均數最高的一個] [http://data.stackexchange.com/stackoverflow/query/61353/tags-with-highest-average因爲儘管它們可能並不總是那麼容易,但人們確實因爲努力而獲得獎勵。 – hammar 2012-02-12 00:02:24

回答

18

由於IO類型處於負值,因此無法對所有MonadIO實例進行一般化處理。關於hackage,有一些庫針對特定實例執行此操作(monad-control,),但是他們是否在語義上聽起來有些爭議,尤其是關於它們如何處理異常和類似奇怪的事情。

編輯:有些人似乎對正面/負面的立場區分感興趣。實際上,沒有什麼可說的了(你可能已經聽說過了,但是以不同的名字)。術語來自分類的世界。

亞型背後的直覺是,「ab一個亞型(我會寫a <= b)當a可以在任何地方使用b有望代替」。在很多情況下確定子類型是很直接的;對於產品,(a1, a2) <= (b1, b2),例如a1 <= b1a2 <= b2,這是一個非常簡單的規則。但是有一些棘手的案例。例如,我們應該什麼時候決定a1 -> a2 <= b1 -> b2

那麼,我們有一個功能f :: a1 -> a2和一個預期功能類型爲b1 -> b2的上下文。所以上下文將使用f的返回值,就好像它是一個b2,因此我們必須要求a2 <= b2。棘手的是,上下文將提供fb1,即使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

e1處於d1 -> e1正位置,其又在的f1類型的正位置;此外,e1總體上處於f1類型的正面位置(因爲它是協變的,根據上述事實)。它在整個學期中的位置是它在每個子項中的位置的結果:positive * positive = positive。類似地,d1d1 -> e1中處於負位置,這在整個類型中處於積極的位置。負值*正值=負值,並且d變量確實是逆變。 b1在類型a1 -> b1中處於正位置,在(a1 -> b1) -> c1中處於負位置,在整個類型中處於負位置。正面*負面*負面=正面,而且是協變的。你的想法)

現在,讓我們來看看MonadIO類:

class Monad m => MonadIO m where 
    liftIO :: IO a -> m a 

我們可以認爲這是子類型的顯式聲明:我們都給人一種方法,使IO a是一個亞型m a部分混凝土m。馬上我們知道我們可以用IO構造函數取正值,並將它們變成m s。但僅此而已:我們無法將負面的IO構造函數轉換爲m - 我們需要一個更有趣的類。

+7

「由於」IO「類型處於負面狀態「 - 你能詳細說明這意味着什麼以及它爲什麼重要嗎? – 2012-02-11 21:36:40

+2

@丹伯頓我已經寫了一些關於它的文章 – 2012-02-12 01:04:59

+0

這當然有幫助,我認爲monad-control可以讓我做足夠的修補。在這種情況下,沒有看到從「ma」到「tma」的上下文變化會如何破壞任何東西。由於缺少其他答案,我將其設置爲接受。 – zeus 2012-02-16 21:37:36