适用的变压器类

Cir*_*dec 8 haskell monad-transformers applicative

Applicative变压器类在哪里?我想在之前的答案中使用变压器类的变压器类,但它们似乎不存在.

变压器包和许多其他的全是保护者变压器的Applicative结构,即使在底层结构不是Monad.

快速浏览一下transformersApplicative大部分变压器的实例.

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)
Run Code Online (Sandbox Code Playgroud)

只有状态和替换的变换器(ExceptTMaybeT)需要Applicative实例的底层monad .

(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)
Run Code Online (Sandbox Code Playgroud)

有一类Monad变形金刚.我可以看到某些东西可能需要这种Monad约束,因为它不能在其他地方引入.

class MonadTrans t where
    lift :: (Monad m) => m a -> t m a
Run Code Online (Sandbox Code Playgroud)

Applicative变压器的课程在哪里?

class ApTrans t where
    liftAp :: (Applicative f) => f a -> t f a
Run Code Online (Sandbox Code Playgroud)

或者只是普通的旧变形金刚(虽然我无法想象有任何法律规定)?

class Trans t where
    liftAny :: f a -> t f a
Run Code Online (Sandbox Code Playgroud)

由于仅在多态约束方面存在差异,因此这些类型类具有奇怪的方差模式.除了他们的法律,这要考虑unexpressible约束,任何的实例Trans应自动实例ApTransMonadTrans,以及任何的实例ApTrans应自动的一个实例MonadTrans.

如果我们转到mtl库,那里的类也与Applicative变换器堆栈不兼容.我熟悉的所有mtl类都有Monad约束.例如,这是MonadReader

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)
Run Code Online (Sandbox Code Playgroud)

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

我会天真地写出类似的东西

class Reader r m | m -> r where
    ask :: m r
    local :: (r -> r) -> m a -> m a
Run Code Online (Sandbox Code Playgroud)

甚至分成local一个单独的类.

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
Run Code Online (Sandbox Code Playgroud)

local没有Monad实例可能很难使用.没有Monad约束的更有用的界面就像是

class (Reader r m) => Local r m | m -> r where
    local :: m (r -> r) -> m a -> m a
Run Code Online (Sandbox Code Playgroud)

是否存在没有Monad约束的现有变压器类,或者是否真的需要另一个变压器类库?

J. *_*son 9

与Monads不同,应用程序在产品和组成下是封闭的,因此不需要像"变形金刚"这样的特殊类别的东西.这是一个小型图书馆:

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)
Run Code Online (Sandbox Code Playgroud)

此外,所有monad 都是 Applicatives所以我们应该能够重用该代码.遗憾的是,缺少Applicative-Monad子类型会使monadic代码更加排斥,而不是需要这样的代码.如果所有这些库都要求(Applicative m, Monad m)约束,它本可以纠正,但它们不会.此外,考虑到你可能不得不写的频率

(MonadReader m, Monad m) => ...
Run Code Online (Sandbox Code Playgroud)

Monad超类约束很方便.但我不确定这是完全必要的.

  • @PetrPudlák值得注意的是,除非我们从"一边到另一边"进行环境自然转换,否则申请人不会结算金额.由于他们需要偏见,因此它们也是非规范的. (2认同)
  • 真棒,我在链接答案中定义的'LetT'变换器就是'Compose(Ap Identity)' (2认同)

Lui*_*las 5

正如J. Abrahamson所说,应用程序在产品和组成下是封闭的,因此不需要专用的变压器版本.但是,也没有必要推出自己的Applicative产品/组合类型,因为平台已经有这些:

我发现使用这些更简单的方法是GeneralizedNewtypeDeriving扩展,因为那样你就可以定义这样的类型:

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...
Run Code Online (Sandbox Code Playgroud)

Applicative工具集中另一个有用的工具是免费的applicative functor.我通常使用Edward Kmett的free库版本,但是如果你想要更少的依赖,那么很容易自己动手.

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

{-# 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
Run Code Online (Sandbox Code Playgroud)