Существует много разговоров о том, что 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 является одним из них.