В последнее время мое внимание привлекло одно раздражение от ленивого ввода-вывода
import System.IO
import Control.Applicative
main = withFile "test.txt" ReadMode getLines >>= mapM_ putStrLn
where getLines h = lines <$> hGetContents h
Из-за ленивого ввода-вывода вышеуказанная программа ничего не печатает. Поэтому я предположил, что это можно решить с помощью строгой версии 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)