Haskell “一元递归”的抽象;除非;
我正试图找出是否有可能为以下情况编写一个抽象。假设我有一个带有函数Haskell “一元递归”的抽象;除非;,haskell,monads,Haskell,Monads,我正试图找出是否有可能为以下情况编写一个抽象。假设我有一个带有函数a->m Bool的类型a,例如MVar Bool和readMVar。为了抽象出这个概念,我为类型及其函数创建了一个新的类型包装器: newtype MPredicate m a = MPredicate (a,a -> m Bool) 我可以定义一个相当简单的操作,如下所示: doUnless :: (Monad m) => Predicate m a -> m () -> m () doUnless
a->m Bool
的类型a
,例如MVar Bool
和readMVar
。为了抽象出这个概念,我为类型及其函数创建了一个新的类型包装器:
newtype MPredicate m a = MPredicate (a,a -> m Bool)
我可以定义一个相当简单的操作,如下所示:
doUnless :: (Monad m) => Predicate m a -> m () -> m ()
doUnless (MPredicate (a,mg)) g = mg a >>= \b -> unless b g
main = do
b <- newMVar False
let mpred = MPredicate (b,readMVar)
doUnless mpred (print "foo")
是否可以重构foobar
,以便使用MPredicate
和doUnless
忽略foobar'
的实际实现,我可以想出一种简单的方法来做类似的事情:
cycleUnless :: x -> (x -> x) -> MPredicate m a -> m ()
cycleUnless x g mp = let g' x' = doUnless mp (g' $ g x')
in g' $ g x
旁白:我觉得fix
可以用来使上面的内容更整洁,尽管我仍然很难弄清楚如何使用它
但是cycleeunless
在foobar
上不起作用,因为foobar'
的类型实际上是Int->IO()
(使用print x'
)
我还想进一步研究这个抽象,这样它就可以围绕Monad进行线程处理。有状态的单子就更难了。例如
-- EDIT: Updated the below to show an example of how the code is used
{- ^^ some parent function which has the MVar ^^ -}
cycleST :: (forall s. ST s (STArray s Int Int)) -> IO ()
cycleST sta = readMVar mvb >>= \b -> unless b $ do
n <- readMVar someMVar
i <- readMVar someOtherMVar
let sta' = do
arr <- sta
x <- readArray arr n
writeArray arr n (x + i)
return arr
y = runSTArray sta'
print y
cycleST sta'
--编辑:更新了以下内容,以显示如何使用代码的示例
{-^^具有MVar^^-}的某个父函数
循环测试::(对于所有s.ST s(STArray s Int))->IO()
cycleST sta=readMVar mvb>=\b->除非b$do
n我不确定你的MPredicate
在做什么。
首先,与其新键入元组,不如使用普通的algebric数据类型
data MPredicate a m=MPredicate a(a->m Bool)
其次,您使用它的方式,MPredicate
相当于m Bool
。
Haskell是懒散的,因此不需要传递函数及其参数(即使
它对严格的语言很有用)。只需传递结果,就可以在需要时调用该函数
我的意思是,与其四处传递(x,f)
,不如传递fx
当然,如果您不想延迟计算,并且在某个时候确实需要参数或函数以及结果,则可以使用元组
无论如何,如果您的MPredicate
仅用于延迟功能评估,MPredicat
减少到m Bool
和doUnless
到,除非
您的第一个示例完全等效:
main = do
b <- newMVar False
unless (readMVar b) (print "foo")
main=do
b我不确定你的MPredicate
在做什么。
首先,与其新键入元组,不如使用普通的algebric数据类型
data MPredicate a m=MPredicate a(a->m Bool)
其次,您使用它的方式,MPredicate
相当于m Bool
。
Haskell是懒散的,因此不需要传递函数及其参数(即使
它对严格的语言很有用)。只需传递结果,就可以在需要时调用该函数
我的意思是,与其四处传递(x,f)
,不如传递fx
当然,如果您不想延迟计算,并且在某个时候确实需要参数或函数以及结果,则可以使用元组
无论如何,如果您的MPredicate
仅用于延迟功能评估,MPredicat
减少到m Bool
和doUnless
到,除非
您的第一个示例完全等效:
main = do
b <- newMVar False
unless (readMVar b) (print "foo")
main=do
bMPredicate
在这里是相当多余的m Bool
可以替代使用。该软件包包含大量具有m Bool
条件的控制结构whileM_uu
在这里特别适用,尽管我们需要为我们正在处理的Int
包含一个State
monad:
import Control.Monad.State
import Control.Monad.Loops
import Control.Applicative
foobar :: MVar Bool -> IO ()
foobar mvb = (`evalStateT` (0 :: Int)) $
whileM_ (not <$> lift (readMVar mvb)) $ do
modify (+1)
lift . print =<< get
lift $ threadDelay 1000000
它在一元设置中更方便、更模块化,因为我们总是可以从纯Bool
到m Bool
,但反之亦然
foobar :: MVar Bool -> IO ()
foobar mvb = go 0
where
go :: Int -> IO ()
go x = unlessM (readMVar mvb) $ do
let x' = x + 1
print x'
threadDelay 1000000
go x'
您提到了fix
;有时人们确实会将其用于特殊的一元循环,例如:
printUntil0 :: IO ()
printUntil0 =
putStrLn "hello"
fix $ \loop -> do
n <- fmap read getLine :: IO Int
print n
when (n /= 0) loop
putStrLn "bye"
MPredicate
在这里是多余的m Bool
可以替代使用。该软件包包含大量具有m Bool
条件的控制结构whileM_uu
在这里特别适用,尽管我们需要为我们正在处理的Int
包含一个State
monad:
import Control.Monad.State
import Control.Monad.Loops
import Control.Applicative
foobar :: MVar Bool -> IO ()
foobar mvb = (`evalStateT` (0 :: Int)) $
whileM_ (not <$> lift (readMVar mvb)) $ do
modify (+1)
lift . print =<< get
lift $ threadDelay 1000000
它在一元设置中更方便、更模块化,因为我们总是可以从纯Bool
到m Bool
,但反之亦然
foobar :: MVar Bool -> IO ()
foobar mvb = go 0
where
go :: Int -> IO ()
go x = unlessM (readMVar mvb) $ do
let x' = x + 1
print x'
threadDelay 1000000
go x'
您提到了fix
;有时人们确实会将其用于特殊的一元循环,例如:
printUntil0 :: IO ()
printUntil0 =
putStrLn "hello"
fix $ \loop -> do
n <- fmap read getLine :: IO Int
print n
when (n /= 0) loop
putStrLn "bye"
您希望将具有副作用、延迟和独立停止条件的有状态操作干净地结合起来
在这些情况下,免费
软件包中的
此monad transformer允许您将(可能是非结束的)计算描述为一系列离散步骤。更好的是,它允许您使用mplus
交错“阶梯式”计算。当任何单个计算停止时,组合计算停止
一些初步进口:
import Data.Bool
import Control.Monad
import Control.Monad.Trans
import Control.Monad.Trans.Iter (delay,untilJust,IterT,retract,cutoff)
import Control.Concurrent
您的foobar
函数可以理解为三件事的“总和”:
- 一种计算,在每一步只从
MVar
读取数据,当MVar
为True
时完成
untilTrue :: (MonadIO m) => MVar Bool -> IterT m ()
untilTrue = untilJust . liftM guard . liftIO . readMVar
- 每一步都有延迟的无限计算
delays :: (MonadIO m) => Int -> IterT m a
delays = forever . delay . liftIO . threadDelay
- 打印一系列递增数字的无限计算
foobar' :: (MonadIO m) => Int -> IterT m a
foobar' x = do
let x' = x + 1
liftIO (print x')
delay (foobar' x')
有了它,我们可以将foobar
写成:
foobar :: (MonadIO m) => MVar Bool -> m ()
foobar v = retract (delays 1000000 `mplus` untilTrue v `mplus` foobar' 0)
最妙的是,您可以很容易地更改或删除“停止条件”和延迟
一些澄清:
delay
函数不是IO中的延迟,它只是