严格的fmap只使用Functor,而不是Monad

Dan*_*ton 10 monads haskell strictness

懒惰IO的一个恼怒最近引起了我的注意

import System.IO
import Control.Applicative

main = withFile "test.txt" ReadMode getLines >>= mapM_ putStrLn
  where getLines h = lines <$> hGetContents h
Run Code Online (Sandbox Code Playgroud)

由于懒惰的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)
Run Code Online (Sandbox Code Playgroud)

更换<$><$!>确实缓解该问题.但是,我不满意.<$!>有一个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)
Run Code Online (Sandbox Code Playgroud)

Dan*_*her 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
Run Code Online (Sandbox Code Playgroud)

而评价:

Prelude Force> test
1
Run Code Online (Sandbox Code Playgroud)

forceM需要一个足够严格的(>>=)实际强制其参数的结果.Functor甚至没有(>>=).我不知道如何写一个有效的forceF.(当然,这并不能证明这是不可能的.)