我正在实现一个括号类型的模式。假设我有一些资源。为了简单起见,我将使用以下代码
data Res = Res Int deriving(Show)
acquire :: IO Res
acquire = do
print "Acquire:"
Res . read <$> getLine
release :: Res -> IO ()
release r = do
print "Result:"
print r
use :: Res -> IO Res
use r = do
print r
let Res x = r in return . Res $ x + 1
字符串
所以我得到了表单的行为
main1 :: IO ()
main1 = do
r <- acquire
r <- use r
release r
型
有没有可能实现一个名为WithRes
的MonadIO
示例,它将为use
保存一个参数,这样use
函数就有了一个更简单的形式
useR :: WithRes ()
useR = ...
acquireR :: WithRes ()
acquireR = ...
releaseR :: WithRes ()
releaseR = ...
runWithRes :: WithRes a -> IO a
main2 :: IO ()
main2 = runWithRes $ do
acquireR
useR
releaseR
型
最后得到main1 = main2
2条答案
按热度按时间a6b3iqyw1#
正如@snak和@willeM_货车Onsem所提到的,我实际上可以使用 State monad。下面是一个快速重写:
字符串
所以
useR
可以写成下面的形式型
现在我们可以在
WithRes
monad之外执行acquire
和release
来设置Res
的初始值(如snak推荐的):型
或者,在我看来,我们可以做得更好。我们可以把
acquire
/release
变成一元动作。型
并添加一个名为
evalWithRes
的简单runWithRes
Package 器,以获得更好的接口型
所以我们可以完全从
WithRes
monad内部运行整个过程。型
注意,我们使用了默认值
Res 0
。避免它的一种方法是将其 Package 到Maybe
中,因此如果它是Nothing
,则必须忽略useR
操作,但这是另一回事。另外,我会注意到,
useR
/acquireR
/releaseR
使用了相同的wrap-unwrapWithRes
值的模式。为了简化编写这样的东西,我们可以添加一些功能,将与状态一起工作,如transformers lib所做的。但对于这个问题,根本没有必要。dldeef672#
我推荐
bracket
+ReaderT
。像这样:字符串
您可以在
useR
中调用ask
或asks
来访问资源。