2012-02-24 12 views
9

Un'irritazione con lazy IO catturato la mia attenzione di recenteFMAP Strict utilizzando solo Functor, non Monade

import System.IO 
import Control.Applicative 

main = withFile "test.txt" ReadMode getLines >>= mapM_ putStrLn 
    where getLines h = lines <$> hGetContents h 

causa pigro IO, il programma stampa sopra nulla. Quindi ho immaginato che questo potesse essere risolto con una versione rigorosa di fmap. E in effetti, sono venuto su con solo un tale combinatore:

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) 

Sostituzione <$> con <$!> fa davvero alleviare il problema. Tuttavia, non sono soddisfatto. <$!> ha un vincolo Monad, che sembra troppo stretto; è compagno <$> richiede solo Functor.

C'è un modo per scrivere <$!> senza il vincolo Monad? Se sì, come? Se no, perché no? Ho provato a gettare severità in tutto il luogo, senza alcun risultato (codice seguente non lavoro come desiderato):

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) 
+2

"(\ x -> seq x x)" è esattamente 'id', quindi non aiuta. –

+0

Mi sembra una definizione molto strana di <$!>. Vorrei andare con "f <$!> m = forceM (liftM f m)" invece. Questo risolve anche il tuo problema, e sembra molto più sensato in altri contesti. Sospetto che non si possa definire questo per i funtori, però. – lpsmith

risposta

8

non credo che sia possibile, e anche la monade forceM non funziona per tutte le monadi:

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 

E la valutazione:

Prelude Force> test 
1 

forceM bisogno di abbastanza stretto (>>=) per forzare effettivamente il risultato del suo argomento. Functor non ha nemmeno un (>>=). Non vedo come si possa scrivere un efficace forceF. (Questo non dimostra che sia impossibile, ovviamente.)