C'è un gran parlare di Applicative
non necessitano di una propria classe di trasformatore, come questo:I trasformatori applicativi sono davvero superflui?
class AppTrans t where
liftA :: Applicative f => f a -> t f a
Ma posso definire trasformatori applicative che non sembrano essere composizioni di applicativi! Ad esempio sideeffectful flussi:
data MStream f a = MStream (f (a, MStream f a))
sollevamento esegue solo l'effetto collaterale ad ogni passo:
instance AppTrans MStream where
liftA action = MStream $ (,) <$> action <*> pure (liftA action)
E se f
è un applicativo, quindi MStream f
è così:
instance Functor f => Functor (MStream f) where
fmap fun (MStream stream) = MStream $ (\(a, as) -> (fun a, fmap fun as)) <$> stream
instance Applicative f => Applicative (MStream f) where
pure = liftA . pure
MStream fstream <*> MStream astream = MStream
$ (\(f, fs) (a, as) -> (f a, fs <*> as)) <$> fstream <*> astream
So che per qualsiasi scopo pratico, f
dovrebbe essere una monade:
joinS :: Monad m => MStream m a -> m [a]
joinS (MStream stream) = do
(a, as) <- stream
aslist <- joinS as
return $ a : aslist
Ma mentre c'è un'istanza Monad
per MStream m
, è inefficiente. (O anche errato?) L'istanza Applicative
è effettivamente utile!
Ora di notare che i flussi usuali nascono come casi particolari per il funtore identità:
import Data.Functor.Identity
type Stream a = MStream Identity a
Ma la composizione di Stream
e f
non è MStream f
! Piuttosto, Compose Stream f a
è isomorfo a Stream (f a)
.
Mi piacerebbe sapere se MStream
è una composizione di due applicazioni qualsiasi.
Edit:
mi piacerebbe offrire un punto di vista teorico categoria. Un trasformatore è un endofuncore "bello" t
nella categoria C
di funtori applicativi (vale a dire fasci monoidali con forza), insieme a una trasformazione naturale liftA
dall'identità su C
a t
. La domanda più generale è ora quali esistono trasformatori utili che non sono della forma "comporre con g
" (dove g
è un applicativo). La mia richiesta è che MStream
sia uno di questi.