懒惰IO的一个恼怒最近引起了我的注意
import System.IO
import Control.Applicative
main = withFile "test.txt" ReadMode getLines >>= mapM_ putStrLn
where getLines h = lines <$> hGetContents h
由于懒惰的IO,上面的程序什么都不打印。所以我想象这可以用fmap
的严格版本来解决。事实上,我确实提出了这样一个组合器:
forceM :: Monad m => m a -> m a
forceM m = do v <- m; return $! v
(<$!>) :: Monad m => (a -> b) -> m a -> m b
f <$!> m = liftM f (forceM m)
用<$>
替换<$!>
确实可以缓解这个问题。但是,我不满意。 <$!>
有Monad
约束,感觉太紧;它的随播广告<$>
仅需Functor
。
有没有办法在没有<$!>
约束的情况下编写Monad
?如果有,怎么做?如果没有,为什么不呢?我试图在整个地方投掷严格,但无济于事(以下代码不按要求工作):
forceF :: Functor f => f a -> f a
forceF m = fmap (\x -> seq x x) $! m
(<$!>) :: Functor f => (a -> b) -> f a -> f b
f <$!> m = fmap (f $!) $! (forceF $! m)
答案 0 :(得分:8)
我不认为这是可能的,而monadic forceM
也不适用于所有monad:
module Force where
import Control.Monad.State.Lazy
forceM :: Monad m => m a -> m a
forceM m = do v <- m; return $! v
(<$!>) :: Monad m => (a -> b) -> m a -> m b
f <$!> m = liftM f (forceM m)
test :: Int
test = evalState (const 1 <$!> undefined) True
评价:
Prelude Force> test
1
forceM
需要足够严格的(>>=)
来实际强制其参数的结果。 Functor
甚至没有(>>=)
。我不知道如何写一个有效的forceF
。 (当然,这并不能证明这是不可能的。)