常见的初学者错误是看return
并认为它是一个语言关键字,它以退货值退出当前函数。当然,我们知道它根本不是它的功能。但我想知道......我们真的可以制作这样的功能吗?纯粹是出于争论的缘故,在这一点上。
似乎我们正在寻找一些具有功能的monad Foo
exit :: x -> Foo x
将中止剩余的计算并立即返回x
。
这样的事情是否可以构建?如果它可以构建,它会有用吗?这甚至是一个理智的事情吗?
答案 0 :(得分:7)
是的,可以在Cont
monad中使用。 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
事实上,您可以在几乎任何monad中执行此操作(例如,您可以在Either
monad中执行此操作,但不能在State
monad中执行此操作。然而,Cont
monad是所有monad的母亲:http://blog.sigfpe.com/2008/12/mother-of-all-monads.html
答案 1 :(得分: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"
需要注意的重要一点是,救助时给出的类型不是免费的 - 它必须与Either的类型相匹配。
答案 2 :(得分:1)
如果你想满足monad法则,你肯定不能称之为return
,因为我们需要return a >>= f = f a
。
另外,我认为根据给出的精确规格,它根本不存在。假设x :: a
和f :: a -> Foo b
,然后只是按照类型exit a >>= f :: Foo b
,我们希望它生成x :: a
。
答案 3 :(得分: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
注意monad有一个额外的类型参数;我认为不可能避免这种情况。基本上Foo e x
生成x
并且“最终”生成e
。请注意,run
函数需要两种类型匹配;您可以同样要求run
仅在exit
被某个地方调用时才有效,或者其他一些可能性......
所以,是的,你可以构建这个,不,它不是特别有用的AFAIK。