Существует много разговоров о том, что Applicative
не нужен собственный трансформаторный класс, например:
class AppTrans t where
liftA :: Applicative f => f a -> t f a
Но я могу определить аппликативные трансформаторы, которые, похоже, не являются композициями аппликаций! Например, побочные потоки:
data MStream f a = MStream (f (a, MStream f a))
Подъем только выполняет побочный эффект на каждом шагу:
instance AppTrans MStream where
liftA action = MStream $ (,) <$> action <*> pure (liftA action)
И если f
является аппликативным, то MStream f
также:
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
Я знаю, что для любых практических целей f
должна быть монадой:
joinS :: Monad m => MStream m a -> m [a]
joinS (MStream stream) = do
(a, as) <- stream
aslist <- joinS as
return $ a : aslist
Но пока есть экземпляр Monad
для MStream m
, он неэффективен. (Или даже неверно?) Экземпляр Applicative
действительно полезен!
Теперь заметим, что обычные потоки возникают как частные случаи для тождественного функтора:
import Data.Functor.Identity
type Stream a = MStream Identity a
Но состав Stream
и f
не MStream f
! Скорее, Compose Stream f a
изоморфно Stream (f a)
.
Я хотел бы знать, является ли MStream
композиция из любых двух аппликаций.
Edit:
Я бы хотел предложить теоретическую точку зрения. Трансформатор - это "хороший" endofunctor t
в категории C
аппликативных функторов (т.е. Слабых моноидальных функторов с силой) вместе с естественным преобразованием liftA
из тождества на C
до t
. Более общий вопрос заключается в том, какие существуют пригодные трансформаторы, которые не имеют формы "compose with g
" (где g
является аппликативным). Я утверждаю, что MStream
является одним из них.