lob*_*ism 8 monads haskell template-haskell
我甚至不确定任何一种单子都有可能; 它违反了monad法律吗?但似乎在某种构造或其他方面应该是可能的.具体是有什么方法可以写一些我可以写的东西
do
someOp ()
someOtherOp ()
thirdOp ()
Run Code Online (Sandbox Code Playgroud)
它会打印出来
step 1 of 3
step 2 of 3
step 3 of 3
Run Code Online (Sandbox Code Playgroud)
这需要模板Haskell还是monad工作?(如果需要Template Haskell,那么怎么做?)
dan*_*iaz 10
我假设您希望自动显示步骤,而不必使用日志语句将代码洒在上面.
使用monad进行此操作的问题在于它们太灵活:在任何时候,其余计算的"形状"可能取决于计算本身期间获得的值.这是明确的类型(>>=),即m a -> (a -> m b) -> m b.
因此,N在运行计算之前,您无法知道固定的总步数.
然而,Haskell提供了另外两个抽象,它们交换monad的一些功能和灵活性,以便有机会事先执行更大量的"静态"分析:applicative functor和arrows.
应用仿函数虽然非常有用,但对于您的需求而言可能太"弱".您无法在applicative functor中编写一个函数,当应用于某个值时,该函数会将该值打印到控制台.这在文章中解释为"成语是遗忘的,箭头是一丝不苟的,单子是混杂的",其中包含了每个抽象限制的一些有启发性的例子(在该论文中,应用函子被称为"成语").
箭头在表达能力和静态分析的适应性之间提供了更好的折衷.箭头计算的"形状"在静态管道中是固定的.在计算过程中获得的数据可以影响管道中稍后的效果(例如,您可以打印通过计算中的先前效果获得的值),但不会更改管道的形状或步数.
因此,如果您可以使用Kleisli箭头(monad的箭头)表达您的计算,也许您可以编写某种箭头变换器(不是 monad变换器),它增加了自动记录功能.
该箭头包提供了一些箭头变压器.我认为StaticArrow可用于自动跟踪总步数.但是你仍然需要编写一些功能来实际发出消息.
编辑:这是一个如何使用箭头计算计算中步数的示例:
module Main where
import Data.Monoid
import Control.Monad
import Control.Applicative
import Control.Arrow
import Control.Arrow.Transformer
import Control.Arrow.Transformer.Static
type SteppedIO a b = StaticArrow ((,) (Sum Int)) (Kleisli IO) a b
step :: (a -> IO b) -> SteppedIO a b
step cmd = wrap (Sum 1, Kleisli cmd)
countSteps :: SteppedIO a b -> Int
countSteps = getSum . fst . unwrap
exec :: SteppedIO a b -> a -> IO b
exec = runKleisli . snd . unwrap
program :: SteppedIO () ()
program =
step (\_ -> putStrLn "What is your name?")
>>>
step (\_ -> getLine)
>>>
step (putStrLn . mappend "Hello, ")
main :: IO ()
main = do
putStrLn $ "Number of steps: " ++ show (countSteps program)
exec program ()
Run Code Online (Sandbox Code Playgroud)
请注意,步骤3的效果受到步骤2中生成的值的影响.使用应用程序无法完成此操作.
我们确实使用了编译静态信息(,) (Sum Int)所需的应用程序StaticArrow(这里只是步骤数).
在执行步骤时显示这些步骤需要更多的工作.
编辑#2如果我们正在处理一系列命令,其中没有效果取决于前一个效果产生的值,那么我们可以避免使用箭头并仅使用应用仿函数来计算步数:
module Main where
import Data.Monoid
import Control.Applicative
import Data.Functor.Compose
type SteppedIO a = Compose ((,) (Sum Int)) IO a
step :: IO a -> SteppedIO a
step cmd = Compose (Sum 1, cmd)
countSteps :: SteppedIO a -> Int
countSteps = getSum . fst . getCompose
exec :: SteppedIO a -> IO a
exec = snd . getCompose
program :: SteppedIO ()
program =
step (putStrLn "aaa")
*>
step (putStrLn "bbb")
*>
step (putStrLn "ccc")
main :: IO ()
main = do
putStrLn $ "Number of steps: " ++ show (countSteps program)
exec program
Run Code Online (Sandbox Code Playgroud)
Data.Functor.Compose来自transformers包裹.
编辑#3以下代码扩展了上Applicative一步计数解决方案,使用pipes包实际发出通知.基于箭头的解决方案可以以类似的方式进行调整.
module Main where
import Data.Monoid
import Control.Applicative
import Control.Monad.State
import Data.Functor.Compose
import Pipes
import Pipes.Lift
type SteppedIO a = Compose ((,) (Sum Int)) (Producer () IO) a
step :: IO a -> SteppedIO a
step cmd = Compose (Sum 1, yield () *> lift cmd)
countSteps :: SteppedIO a -> Int
countSteps = getSum . fst . getCompose
exec :: SteppedIO a -> Producer () IO a
exec = snd . getCompose
stepper :: MonadIO m => Int -> Consumer () m a
stepper n = evalStateP 0 $ forever $ do
await
lift $ modify succ
current <- lift get
liftIO $ putStrLn $ "step " ++ show current ++ " of " ++ show n
program :: SteppedIO ()
program = *** does not change relative to the previous example ***
main :: IO ()
main = runEffect $ exec program >-> stepper (countSteps program)
Run Code Online (Sandbox Code Playgroud)
虽然我认为丹尼尔·迪亚斯"箭头的解决办法是要做到这一点,他完美的方式,有足够的肯定更简单的一个(其中,我刚才看到,他还表示在注释的话)提供的,在你的榜样,没有数据传递之间不同的函数调用.
请记住,由于Haskell很懒惰,函数可以执行许多需要其他语言中的宏的东西.特别是,拥有一系列IO动作没有任何问题.(也绝对安全:由于纯粹,在Haskell中没有办法可以"早点离开"!)然后你可以简单地将这个列表的长度作为总计数,将其与打印语句交错,然后完成.所有的核心语言,不需要TH!
sequenceWithStepCount :: [IO()] -> IO()
sequenceWithStepCount actions = go actions 0
where nTot = length actions
go [] _ = putStrLn "Done!"
go (act:remains) n = do
putStrLn ("Step "++show n++" of "++show nTot)
act
go remains $ succ n
Run Code Online (Sandbox Code Playgroud)
要用得像
do
sequenceWithStepCount [
someOp ()
, someOtherOp ()
, thirdOp ()
]
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
353 次 |
| 最近记录: |