一個常見的初學者的錯誤是看到return
,並認爲它是一個用返回值退出當前函數的語言關鍵字。當然,我們知道這不是它所做的。但我想知道......我們能否真的使這樣的功能?純粹爲了論證的緣故,在這一點上。具有「真實」返回功能的monad
看來,我們正在尋找一些單子Foo
它擁有功能
exit :: x -> Foo x
這將中止計算的休息,立即返回x
。
這樣的事情是可以修復的嗎?如果可以構建,是否有用?這是一種理智的嘗試嗎?
一個常見的初學者的錯誤是看到return
,並認爲它是一個用返回值退出當前函數的語言關鍵字。當然,我們知道這不是它所做的。但我想知道......我們能否真的使這樣的功能?純粹爲了論證的緣故,在這一點上。具有「真實」返回功能的monad
看來,我們正在尋找一些單子Foo
它擁有功能
exit :: x -> Foo x
這將中止計算的休息,立即返回x
。
這樣的事情是可以修復的嗎?如果可以構建,是否有用?這是一種理智的嘗試嗎?
是的,它可能在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
這看起來比我的想法更清潔...... – MathematicalOrchid
'Cont'對於這個方式來說是過度殺傷力。只要使用'或者'或者更簡單的東西。 – shachaf
另外,你可以在任何monad中做到這一點。 (「Cont」是「所有monads的母親」也是不正確的 - 這實際上是Codensity - 但它足夠接近。無論哪種方式,這應該可能暗示你*不應該在*時使用它更簡單的事情正是你想要的。) – shachaf
如果你想滿足單子法,你當然不能稱之爲return
,因爲我們需要return a >>= f = f a
。
此外,我不認爲它可以存在的所有精確的規範給出。假設x :: a
和f :: a -> Foo b
,然後只是跟隨類型exit a >>= f :: Foo b
,但我們希望它產生x :: a
。
看樣子你可以只是實現這一點:
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。
那不就是那個'Either'單子嗎? – bennofs
這正是'Either'單子,它通過'Error'別名大量使用。 – bheklilr
@bennofs:可能,是的。 – MathematicalOrchid
當然這是可行的。它甚至已經在標準庫中。你只需要一個稍微清晰的類型簽名。
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"
需要注意的重要一點是,救助時所給出的類型不是免費的 - 它必須符合任一類型。
一旦你坐下來寫出類型,就會變得很明顯...... – MathematicalOrchid
從技術上講, t匹配問題所需的類型:-) –
@GaneshSittampalam是的。這就是爲什麼我說你需要更好的類型。 – Carl
你當然可以在繼續monad中構建類似的東西,除了返回類型不是你寫的。 – copumpkin