10

最近对惰性 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)
4

1 回答 1

8

我认为这是不可能的,而且单子forceM并不适用于所有单子:

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. (当然,这并不能证明这是不可能的。)

于 2012-02-24T01:01:55.913 回答