应用变压器是否真的是多余的? [英] Are applicative transformers really superfluous?
问题描述
有很多关于 Applicative
not 的讨论需要它自己的变换器类,如下所示:
class AppTrans t其中
liftA :: Applicative f => f a - > t f a
但我可以定义应用变形金刚,看起来不是应用变体的组合!例如 sideeffectful streams :
data MStream fa = MStream(f(a,MStream fa))
提升仅在每一步执行副作用:
实例AppTrans MStream其中
liftA action = MStream $(,)< $>动作< *> pure(liftA action)
如果 f
是一个应用程序,那么 MStream f
也是:
instance Functor f => Functor(MStream f)其中
fmap fun(MStream stream)= MStream $(\(a,as) - >(fun a,fmap fun as))< $>流
实例f => Applicative(MStream f)其中
pure = liftA。纯
MStream fstream< *> MStream astream = MStream
$(\(f,fs)(a,as) - >(f a,fs * as))< $> fstream< *> astream
我知道任何实际用途, f
应该是monad:
joinS :: Monad m => MStream m a - > m [a]
joinS(MStream stream)= do
(a,as)< - stream
aslist< - joinS as
return $ a:aslist
但是当<$ c c> Monad 实例$ c> MStream m ,效率不高。 (或者甚至是不正确的?) Applicative
实例实际上是有用的!
pre $ $ $ c $ import import Data.Functor.Identity
类型Stream a = MStream Identity a
但是 Stream
和 f
不是 MStream f
!相反, Compose Stream fa
同构于 Stream(fa)
。
我想知道 MStream
是否是任何两个应用程序的组合。
编辑:
我想提供一个类别理论观点。变形是应用函子的类别 C
上的一个很好的endofunctor t
>(即具有强度的松散monoidal函子) ,以及从 C
到 t $ c的标识自然转换
liftA
$ C>。更普遍的问题是,现在有哪些有用的转换器不是与 g
(其中 g
是一个应用)。我的观点是 MStream
就是其中之一。
!我相信这个问题有两个不同的部分:
- 将现有的应用程序或monad组合成更复杂的应用程序。
- 构建所有应用程序/ monad。 p> Ad 1. .: Monad变压器对于组合单声道是必不可少的。 Monads 不要直接撰写 >。似乎需要由monad变换器提供的额外信息来说明每个monad如何与其他monad组合(但它可能是这种信息已经存在,请参阅是否有monad没有相应的monad变压器?)。
另一方面, 应用程序直接撰写,请参阅 Data.Functor.Compose 。这就是为什么不需要应用变压器的组成。他们也在产品(但不是 coproduct ) 。
例如, infinite streams
data Stream a = Cons a(Stream a)
和另一个应用g
,都是Stream(ga)
和g(Stream a)
是适用的。
但即使
Stream 也是monad(
join
)的对角线一个二维流),它与另一个monadm
的组合将不会是,既不是Stream(ma)
也不是m(Stream a)
将永远是一个单子。
此外,正如我们所看到的,它们都不同F从你的
MStream g
(这非常接近ListT
done right ),因此:
广告2:所有应用程序是否可以从一些给定的基元集?显然不是。一个问题是构造和数据类型:如果
f
和g
是可应用的,则Either(fa )(ga)
不会,因为我们不知道如何撰写右侧h<>左x
。
另一个构造原语采用了一个固定点,如
MStream
例子。在这里,我们可以尝试通过定义类似于
newtype Fix1 fa = Fix1 {unFix1 :: f(Fix1 f) a}
instance(Functor(f(Fix1 f)))=> Funder(Fix1 f)其中
fmap f(Fix1 a)= Fix1(fmap f a)
instance(Applicative(f(Fix1 f)))=> Applicative(Fix1 f)其中
pure k = Fix1(纯k)
(Fix1 f)* (Fix1x)= Fix1(f * x)
(它不需要-nice
UndecidableInstances
)然后是
data MStream'fga = MStream f(a,ga))
类型MStream f = Fix1(MStream'f)
There is a lot of talk about
Applicative
not needing its own transformer class, like this:class AppTrans t where liftA :: Applicative f => f a -> t f a
But I can define applicative transformers that don't seem to be compositions of applicatives! For example sideeffectful streams:
data MStream f a = MStream (f (a, MStream f a))
Lifting just performs the side effect at every step:
instance AppTrans MStream where liftA action = MStream $ (,) <$> action <*> pure (liftA action)
And if
f
is an applicative, thenMStream f
is as well: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
I know that for any practical purposes,
f
should be a monad:joinS :: Monad m => MStream m a -> m [a] joinS (MStream stream) = do (a, as) <- stream aslist <- joinS as return $ a : aslist
But while there is a
Monad
instance forMStream m
, it's inefficient. (Or even incorrect?) TheApplicative
instance is actually useful!Now note that usual streams arise as special cases for the identity functor:
import Data.Functor.Identity type Stream a = MStream Identity a
But the composition of
Stream
andf
is notMStream f
! Rather,Compose Stream f a
is isomorphic toStream (f a)
.I'd like to know whether
MStream
is a composition of any two applicatives.Edit:
I'd like to offer a category theoretic viewpoint. A transformer is a "nice" endofunctor
t
on the categoryC
of applicative functors (i.e. lax monoidal functors with strength), together with a natural transformationliftA
from the identity onC
tot
. The more general question is now what useful transformers exist that are not of the form "compose withg
" (whereg
is an applicative). My claim is thatMStream
is one of them.解决方案Great question! I believe there are two different parts of this question:
- Composing existing applicatives or monads into more complex ones.
- Constructing all applicatives/monads from some given starting set.
Ad 1.: Monad transformers are essential for combining monads. Monads don't compose directly. It seems that there needs to be an extra bit of information provided by monad transformers that tells how each monad can be composed with other monads (but it could be this information is already somehow present, see Is there a monad that doesn't have a corresponding monad transformer?).
On the other hand, applicatives compose directly, see Data.Functor.Compose. This is why don't need applicative transformers for composition. They're also closed under product (but not coproduct).
For example, having infinite streams
data Stream a = Cons a (Stream a)
and another applicativeg
, bothStream (g a)
andg (Stream a)
are applicatives.But even though
Stream
is also a monad (join
takes the diagonal of a 2-dimensional stream), its composition with another monadm
won't be, neitherStream (m a)
norm (Stream a)
will always be a monad.Furthermore as we can see, they're both different from your
MStream g
(which is very close toListT
done right), therefore:Ad 2.: Can all applicatives be constructed from some given set of primitives? Apparently not. One problem is constructing sum data types: If
f
andg
are applicatives,Either (f a) (g a)
won't be, as we don't know how to composeRight h <*> Left x
.Another construction primitive is taking a fixed point, as in your
MStream
example. Here we might attempt to generalize the construction by defining something likenewtype Fix1 f a = Fix1 { unFix1 :: f (Fix1 f) a } instance (Functor (f (Fix1 f))) => Functor (Fix1 f) where fmap f (Fix1 a) = Fix1 (fmap f a) instance (Applicative (f (Fix1 f))) => Applicative (Fix1 f) where pure k = Fix1 (pure k) (Fix1 f) <*> (Fix1 x) = Fix1 (f <*> x)
(which requires not-so-nice
UndecidableInstances
) and thendata MStream' f g a = MStream (f (a, g a)) type MStream f = Fix1 (MStream' f)
这篇关于应用变压器是否真的是多余的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!