首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >应用变压器类

应用变压器类
EN

Stack Overflow用户
提问于 2014-09-12 00:54:07
回答 2查看 473关注 0票数 10

Applicative变压器类在哪里?我想为前一个答案中的应用转换器堆栈使用转换器类,但它们似乎并不存在。

变压器包和许多其他的转换器都保留了Applicative结构,即使底层结构不是Monad

快速浏览一下transformers,大多数变压器都有Applicative实例。

代码语言:javascript
复制
Applicative f => Applicative (Backwards f)
Applicative f => Applicative (Lift f)
Applicative (ContT r m)
Applicative m => Applicative (IdentityT m)
Applicative m => Applicative (ReaderT r m)
(Monoid w, Applicative m) => Applicative (WriterT w m)
(Applicative f, Applicative g) => Applicative (Compose f g)
(Applicative f, Applicative g) => Applicative (Product f g)

只有用于状态和交替的转换器(ExceptTMaybeT)才需要用于Applicative实例的底层monad。

代码语言:javascript
复制
(Functor m, Monad m) => Applicative (ExceptT e m)
(Functor m, Monad m) => Applicative (MaybeT m)
(Monoid w, Functor m, Monad m) => Applicative (RWST r w s m)
(Functor m, Monad m) => Applicative (StateT s m)

有一个类别的Monad变压器。我可以看到某些东西是如何需要这个Monad约束的,因为它不能在其他地方引入。

代码语言:javascript
复制
class MonadTrans t where
    lift :: (Monad m) => m a -> t m a

Applicative变压器的课程在哪里?

代码语言:javascript
复制
class ApTrans t where
    liftAp :: (Applicative f) => f a -> t f a

或者只是普通的旧变压器(虽然我无法想象这方面有什么法律)?

代码语言:javascript
复制
class Trans t where
    liftAny :: f a -> t f a

由于仅在多态约束上的差异,这类类型有一个奇怪的方差模式。除了必须考虑不可表达的约束的法律之外,任何作为Trans实例的都应该自动成为ApTransMonadTrans的实例,而任何作为ApTrans实例的实例都应该自动成为MonadTrans的实例。

如果我们继续讨论mtl库,那里的类也与Applicative转换堆栈不兼容。我熟悉的所有mtl类都有一个Monad约束。例如,下面是MonadReader

代码语言:javascript
复制
class Monad m => MonadReader r m | m -> r where
    -- | Retrieves the monad environment.
    ask   :: m r
    ask = reader id

    -- | Executes a computation in a modified environment.
    local :: (r -> r) -- ^ The function to modify the environment.
          -> m a      -- ^ @Reader@ to run in the modified environment.
          -> m a

    -- | Retrieves a function of the current environment.
    reader :: (r -> a) -- ^ The selector function to apply to the environment.
           -> m a
    reader f = do
      r <- ask
      return (f r)

Monad约束的目的是什么?它使上述许多变压器的MonadReaderMonadReader实例与Applicative转换器堆栈不兼容。

我会天真地写这样的东西

代码语言:javascript
复制
class Reader r m | m -> r where
    ask :: m r
    local :: (r -> r) -> m a -> m a

甚至将local分割成一个单独的类。

代码语言:javascript
复制
class Reader r m | m -> r where
    ask :: m r

class (Reader r m) => Local r m | m -> r where
    local :: (r -> r) -> m a -> m a

如果没有local实例,很难使用Monad。没有Monad约束的更有用的接口应该如下所示

代码语言:javascript
复制
class (Reader r m) => Local r m | m -> r where
    local :: m (r -> r) -> m a -> m a

是否存在没有Monad约束的现有转换器类,或者是否实际需要另一个转换器类库?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2014-09-12 22:04:17

正如J. Abrahamson所说,Applicatives在产品和组合中是封闭的,因此不需要专门的变压器版本。但是,也不需要滚动您自己的应用程序产品/组合类型,因为平台已经有以下这些:

我发现使用这些类型的更容易的方法是使用GeneralizedNewtypeDeriving扩展,因为这样您就可以定义如下类型:

代码语言:javascript
复制
newtype MyType m a = MyType (Compose (Const m) (Reader m) a)
    deriving (Functor, Applicative)

-- Plus a bunch of utility definitions to hide the use of Compose and generally
-- keep you sane...

在应用工具集中的另一个有用的工具是自由应用函子。我通常使用图书馆版本,但是如果您想要减少依赖项,那么很容易使用您自己的。

这些定义也很有用(尽管我欢迎关于命名方案的建议,特别是"I/O“位):

代码语言:javascript
复制
{-# LANGUAGE Rank2Types, TypeOperators #-}

import Control.Applicative
import Data.Functor.Compose

-- | A handy infix type synonym for 'Compose', which allows us to
-- stack 'Applicative's with less syntactic noise:
-- 
-- > type CalculationT s p f = Reader (Frame s p) :. Reader (Cell s p) :. f
-- > type Calculation s p = Calculation s p Identity
--
-- Note that 'Identity' and ':.' form something a type-level monoid
-- modulo @newtype@ equivalence.  The following isomorphisms hold:
--
-- > f :. Identity  ~=  Identity :. f  ~=  f
-- > f :. g :. h  ~=  (f :. g) :. h 
--
type f :. g = Compose f g
infixr :.

-- | Lift an action from the outer functor into the composite.
-- Alternative reading: append an 'Applicative' to the right of @f@.
liftO :: (Functor f, Applicative g) => f a -> (f :. g) a
liftO = Compose . fmap pure

-- | Lift an action from the inner functor into the composite.
-- Alternative reading: prepend an 'Applicative' to the left of @g@.
liftI :: Applicative f => g a -> (f :. g) a
liftI = Compose . pure

-- | Lift a natural transformation from @g@ to @h@ into a morphism
-- from @f :. g@ to @h :. g@.
hoistO :: (forall x. f x -> h x) -> (f :. g) a -> (h :. g) a
hoistO eta = Compose . eta . getCompose

-- | Lift a natural transformation from @g@ to @h@ into a morphism
-- from @f :. g@ to @f :. h@.
hoistI :: Functor f => (forall x. g x -> h x) -> (f :. g) a -> (f :. h) a
hoistI eta = Compose . fmap eta . getCompose
票数 6
EN

Stack Overflow用户

发布于 2014-09-12 01:59:25

应用程序,不像单体,是封闭的产品和组成,因此不需要一个特殊类别的东西,如“变压器”。这里有一个小图书馆:

代码语言:javascript
复制
data (*) f g x = P (f x) (g x)     deriving Functor
data C   f g x = C (f (g x))       deriving Functor

instance (Applicative f, Applicative g) => Applicative (f * g) where
  pure a = P (pure a) (pure a)
  P ff gf <*> P fx gx = P (ff <*> fx) (gf <*> gx)

instance (Applicative f, Applicative g) => Applicative (C f g) where
  pure = C . pure . pure
  C fgf <*> C fgx = C (liftA2 (<*>) fgf fgx)

而且,所有的monads都是Applicatives,所以我们应该能够重用该代码。可悲的是,缺乏可应用的Monad子类型迫使一元代码比所需的更具有排他性,因此这类代码是非法的。如果所有这些库都要求一个(Applicative m, Monad m)约束,那么它可以被纠正,但它们没有。此外,考虑到你可能不得不经常写

代码语言:javascript
复制
(MonadReader m, Monad m) => ...

Monad超类约束非常方便。不过,我不确定这是完全必要的。

票数 9
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/25799124

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档