2014-01-11 42 views
3

一個常見的初學者的錯誤是看到return,並認爲它是一個用返回值退出當前函數的語言關鍵字。當然,我們知道這不是它所做的。但我想知道......我們能否真的使這樣的功能?純粹爲了論證的緣故,在這一點上。具有「真實」返回功能的monad

看來,我們正在尋找一些單子Foo它擁有功能

exit :: x -> Foo x 

這將中止計算的休息,立即返回x

這樣的事情是可以修復的嗎?如果可以構建,是否有用?這是一種理智的嘗試嗎?

+3

你當然可以在繼續monad中構建類似的東西,除了返回類型不是你寫的。 – copumpkin

回答

7

是的,它可能在Cont單子中。該Cont定義如下:

newtype Cont r a = Cont {runCont :: (a -> r) -> r} 

instance Monad (Cont r) where 
    return a = Cont ($ a) 
    m >>= k = Cont $ \c -> runCont m $ \a -> runCont (k a) c 

現在我們可以創建exit如下:

exit = Cont . const 

事實上,你可以做到這一點在幾乎任何單子(例如,你可以在做Either monad,但不在State monad中)。然而,Cont monad是所有monad的母親:http://blog.sigfpe.com/2008/12/mother-of-all-monads.html

+0

這看起來比我的想法更清潔...... – MathematicalOrchid

+2

'Cont'對於這個方式來說是過度殺傷力。只要使用'或者'或者更簡單的東西。 – shachaf

+0

另外,你可以在任何monad中做到這一點。 (「Cont」是「所有monads的母親」也是不正確的 - 這實際上是Codensity - 但它足夠接近。無論哪種方式,這應該可能暗示你*不應該在*時使用它更簡單的事情正是你想要的。) – shachaf

1

如果你想滿足單子法,你當然不能稱之爲return,因爲我們需要return a >>= f = f a

此外,我不認爲它可以存在的所有精確的規範給出。假設x :: af :: a -> Foo b,然後只是跟隨類型exit a >>= f :: Foo b,但我們希望它產生x :: a

1

看樣子你可以只是實現這一點:

data Foo e x = 
    Next x | 
    Done e 

instance Monad (Foo e) where 
    return = Next 

    (Next x) >>= f = f x 
    (Done e) >>= f = Done e 

exit :: e -> Foo e x 
exit = Done 

run :: Foo x x -> x 
run (Next x) = x 
run (Done x) = x 

注意的單子怎麼有一個額外的類型參數;我認爲這是可以避免的。基本上Foo e x產生一個x並將「最終」產生一個e。請注意,run函數要求兩種類型匹配;你同樣可以要求如果exit被稱爲某處,或者一些其他的可能性一個run只能...

所以,是的,你可以構建這個,不,它不是特別有用AFAIK。

+3

那不就是那個'Either'單子嗎? – bennofs

+3

這正是'Either'單子,它通過'Error'別名大量使用。 – bheklilr

+0

@bennofs:可能,是的。 – MathematicalOrchid

5

當然這是可行的。它甚至已經在標準庫中。你只需要一個稍微清晰的類型簽名。

import Control.Monad 

exit :: a -> Either a b 
exit = Left 

main = print $ do 
    x <- Right 5 
    exit "foo" 
    error "this is a good place to crash" 

需要注意的重要一點是,救助時所給出的類型不是免費的 - 它必須符合任一類型。

+0

一旦你坐下來寫出類型,就會變得很明顯...... – MathematicalOrchid

+0

從技術上講, t匹配問題所需的類型:-) –

+0

@GaneshSittampalam是的。這就是爲什麼我說你需要更好的類型。 – Carl