2010-04-18 17 views
3

我正在嘗試爲未來的項目創建monad變換器,但不幸的是,我的Monad typeclasse的實現(>> =)功能不起作用。嘗試實施(>> =)函數以創建自定義monad變換器時出現錯誤

首先,這裏是底層的單子的實現:

newtype Runtime a = R { 
    unR :: State EInfo a 
} deriving (Monad) 

這裏,單子typeclasse的實現是由GHC自動完成(使用GeneralizedNewtypeDeriving語言編譯)。 該單子轉換被定義爲這樣:

newtype RuntimeT m a = RuntimeT { 
    runRuntimeT :: m (Runtime a) 
} 

的問題來自我實例化的單子typeclasse的(>> =)功能的方式:

instance (Monad m) => Monad (RuntimeT m) where 
    return a = RuntimeT $ (return . return) a 
    x >>= f = runRuntimeT x >>= id >>= f 

我看到它的方式,所述第一個>>=運行在底層m monad中。因此,runRuntimeT x >>=返回Runtime a類型的值(對吧?)。然後,下面的代碼id >>=應返回類型爲a的值。該值是傳遞給類型爲f :: (Monad m) => a -> RuntimeT m b的函數f的值。

問題在於類型問題:f函數的類型與(>> =)函數所需的類型不匹配。我可以讓這個連貫一致嗎?我可以明白爲什麼這不起作用,但我無法將它變成功能性的東西。

編輯:錯誤消息:

Core.hs:34:4: 
    Occurs check: cannot construct the infinite type: m = RuntimeT m 
    When generalising the type(s) for `>>=' 
    In the instance declaration for `Monad (RuntimeT m)' 
Failed, modules loaded: none. 

謝謝你的幫助,不要猶豫,糾正任何缺陷在我的消息,
查理P.

+1

請向我們顯示您收到的錯誤消息。 – dave4420 2010-04-18 12:58:59

回答

3

從裏德巴頓說的關於StateT的後續內容 - 下面是您將如何使用StateT定義RuntimeT的方法。然後,可以使用身份monad來簡單定義montead Runtime

newtype RuntimeT m a = R { 
    unR :: StateT EInfo m a 
} 

type Runtime = RuntimeT Identity 

instance Monad m => Monad (RuntimeT m) where 
    return = R . return 

    (R m) >>= f = R (m >>= unR . f) 
+0

我不知道身份monad存在,它看起來就是我想要的。然而,正如我在評論裏德巴頓的帖子時所說的那樣,創建這個新的monad和monad變換器的想法是將用戶的狀態monad隱藏起來。如何使用您在消息中陳述的內容來實現這一目標? – CharlieP 2010-04-19 12:52:15

+2

@CharlieP:問題在於StateT在s - >和(a,s)之間夾着底層monad,而您嘗試的變壓器未能做到這一點。如果你願意,你可以選擇爲Alasdair提供的新類型派生Monad(甚至是MonadState EInfo),但是如果不使用StateT或手工編碼,你將無法使用Monad作爲變換器。 擴展StateT的定義產生newtype RuntimeT ma = RuntimeT {unRuntimeT :: EInfo - > m(a,EInfo)}但您的定義產生新類型RuntimeT ma = RuntimeT {unRuntimeT :: m(EInfo - >(a,EInfo) )} – 2010-04-19 14:21:42

+2

查理,你通常使用Haskell的模塊系統來隱藏'RuntimeT'的實現細節。假如你不輸出'unR'或'R',用戶不需要知道你使用了'StateT'。 例如,您可能需要一個'RunStateT'等同於'RuntimeT' - 可以實現爲'runRuntimeT = runStateT。 unR'。注意它的類型是'RuntimeT m a - > EInfo - > m(a,EInfo)'。 'StateT'不會出現在任何地方,有人使用這個函數可以在對'StateT'的存在沒有意識的情況下使用它。類似的功能可以寫入get/put。希望有所幫助。 – Alasdair 2010-04-19 18:39:39

4

通常StateT s m單子發送as -> m (a, s),但您正在使用m (s -> (a, s))。我不認爲後者形成一個普通s monad。你確定你不只是想用StateT


這裏的原因,我不認爲am (s -> (a, s))是一個單子:要寫>>=我需要一個函數,它的類型

m (s -> (a, s)) 
a -> m (s -> (b, s)) 

參數和返回類型的值

m (s -> (b, s)) 

結果的「效應」(即fmap (const()))必須是第一個參數的「效應」(因爲我們沒有辦法g等a傳遞給第二個參數。由於m僅出現在結果類型的外部,因此我們無法使用第二個參數來處理任何事情,因此將無法擺脫它引入的m

+0

想象一下,我有一個運行時單子內運行的函數。這個monad是完全不透明的,用戶不應該知道它實際上是一個頂部帶糖的國家monad。 RuntimeT monad變換器背後的想法是,它能夠透明地使用運行時monad中運行的函數,比方說,運行在IO monad中的函數。這就是爲什麼我不能使用StateT monad transfomer而不是我自定義的。 – CharlieP 2010-04-19 12:49:57

+1

感謝您指出我monad不合邏輯的部分,我將使用您和Alasdair所說的方法,並結合monad身份。 – CharlieP 2010-04-19 19:28:10

相關問題