2010-02-09 39 views
4

繼續追求理解ContT和朋友。請考慮以下(荒謬但說明性的)代碼:Haskell與ContT,callCC混淆,當

v :: IO (Either String [String]) 
v = return $ Left "Error message" 

doit :: IO (Either String()) 
doit = (flip runContT return) $ callCC $ \k -> do 
    x <- liftIO $ v 
    x2 <- either (k . Left) return x 
    when True $ k (Left "Error message 2") 
    -- k (Left "Error message 3") 
    return $ Right() -- success 

此代碼不能編譯。但是,如果將when替換爲它下面的註釋k調用,它將進行編譯。這是怎麼回事?

或者,如果我註釋掉x2行,它也會編譯。 ???

顯然,這是原始代碼的蒸餾版本,因此所有元素都有其用途。感謝關於正在發生的事情以及如何解決它的解釋性幫助。謝謝。

+4

「持續追求理解ContT和朋友」?傻瓜的差事!可以理解的延續不是真正的延續。 – 2010-02-09 14:59:00

回答

6

這裏的問題有同類型的wheneither,沒有什麼特別ContT做:

when :: forall (m :: * -> *). (Monad m) => Bool -> m() -> m() 
either :: forall a c b. (a -> c) -> (b -> c) -> Either a b -> c 

的第二個參數必須是m()類型的一些單子m。該when行代碼的可能因此被修改,像這樣:

when True $ k (Left "Error message 2") >> return() 

使代碼編譯。這可能不是你想要做的事情,但它給了我們一個暗示:什麼可能是錯誤的:k的類型已被推斷爲when令人難以接受的東西。

現在爲either簽名:注意到either的兩個參數必須是產生相同類型結果的函數。這裏的return的類型取決於x的類型,該類型依次由v上的明確簽名確定。因此(k . Left)位必須具有相同的類型;這反過來又固定的k類型在(GHC確定的)

k :: Either String() -> ContT (Either String()) IO [String] 

這與when的期望不兼容。

當你註釋掉x2線,然而,它的代碼的類型檢查的視圖效果被移除,所以k不再被迫進入一個不方便的類型,不承擔類型

k :: Either [Char]() -> ContT (Either [Char]()) IO() 

這是好的when的書。因此,代碼編譯。

作爲最後一個筆記,我使用GHCi的斷點工具來獲取在兩種情況下的確切類型k - 我遠遠不夠專業,無法用手寫出它們並以任何方式保證它們的正確性。 :-)使用:break ModuleName line-number column-number來試用它。

+0

謝謝。這是有道理的,但不能解決我的問題。我認爲我的代碼的意圖是明確的。尋找一種方式從IO獲取一些數據,並在各種條件下逃離功能。連續寫這個的正確方法是什麼? – me2 2010-02-09 17:33:05

+0

那麼,其實你可以使用我在答案中提到的技巧,並使用'k(...)>> return()'在'when'位(類似的表達式出現類型不匹配的地方),因爲'>> return()'部分不重要 - 如果此時調用'k',無論如何,這些計算都會退出。我在回答中建議,這可能不是你想要的,但現在我相信正好相反。你可以試一試,讓我知道它是否有幫助?如果是這樣,我會在正確的答案中編輯它。 – 2010-02-11 04:14:37

+0

當然你的代碼現在保證返回'Left'錯誤消息「'無論你使用哪一個小修補程序來編譯它,因爲''''''''''''''''''''''已經被調用。如果你把'v'改成'return $ Right [「Foo」]'並在'when'行加上'>> return()',那麼它會返回'Left'Error message 2「'。 HTH。 – 2010-02-11 04:18:46