是否可以实现这种通用翻转?

时间:2020-02-03 22:33:21

标签: haskell monads reader-monad

我想写一个具有类型签名的对象:

genericFlip ::
  ( MonadReader (o (n c)) m
  , MonadReader a n
  , MonadReader b o
  )
    => m (n (o c))

对于monad读者来说,这本质上是一个翻转。

现在很容易编写如下所示的版本:

genericFlip ::
  ( MonadReader (b -> a -> c) m
  , MonadReader a n
  , MonadReader b o
  )
    => m (n (o c))
  genericFlip = do
    f <- ask
    return $ do
      a <- ask
      return $ do
        b <- ask
        return $ f b a

甚至用(->)代替Reader,但是不管我怎么动脑筋,我似乎都无法做出一个适合所有读者的定义。

是否可以在Haskell中制作一个具有这种类型签名的对象?

3 个答案:

答案 0 :(得分:1)

首先,我建议不要将m概括为MonadReader。以flip ...

的类型
flip :: (a -> b -> c) -> (b -> a -> c)

...中间的箭头与其他箭头不同:它仅将翻转的输入和输出链接在一起。通过这种简化,我们最终为您建议的genericFlip提供了一个简单的类型:

genericFlip :: (MonadReader a n, MonadReader b o) => o (n c) -> n (o c)

在任何情况下,都无法使用此签名来实现genericFlipMonadReader接口本身无法提供一种为计算提供环境的方法,这对于交换层是必不可少的。例如,考虑以下问题中的专门genericFlip

genericFlip' :: (MonadReader a n, MonadReader b o) => (b -> a -> c) -> n (o c)
genericFlip' f = do
  a <- ask
  return $ do
    b <- ask
    return $ f b a

从根本上说,它依赖于f是一个函数,这意味着我们可以为其提供环境(并且,正如您所注意到的,如果我们使用Reader,我们可以通过{{1 }}。最终,runReader所做的一切都是将函数转变为阅读器计算,这种无点拼写使它变得透明:

MonadReader

genericFlip' :: (MonadReader a n, MonadReader b o) => (b -> a -> c) -> n (o c) genericFlip' = fmap reader . reader . flip 的一种概括是distribute

flip

distribute :: (Distributive g, Functor f) => f (g a) -> g (f a) 也称为flap(??),而distribute @((->) _)本身就是distribute @((->) _) @((->) _)

但是,

flip并没有像我们在此问题中希望的那样使我们脱离函数。对于某些特定的Distributive,每个分布函子与(->) r同构。当我们查看r类时,这些连接变得更加明显,该类在原则上等效于Representable,但是使用更复杂的编码,使同构显式。除了Distributivedistribute的泛化之外,我们还有flip作为函数应用程序类似物,还有index,看起来很像tabulate。实际上,该类提供了a default MonadReader implementation,可以方便地通过the Co newtype派生该类。

最后一点,reader尽管不能完全适合我们建议的通用翻转签名,但仍然非常容易翻转,可以归结为ReaderT r (ReaderT s m) a。可以说,这并不是那么有用,但实际上,通常不是将嵌套的阅读器布局嵌套在一起,而是将环境组合成一个单一的类型,并只有一个阅读器层(另请参见the RIO monad)。

答案 1 :(得分:0)

不。这是不可能的。

第一个使o可以遍历的签名没有什么。

答案 2 :(得分:0)

仅依靠MonadReader就可以,但是Traversable可以使您得到:

genericFlip :: (Traversable o, MonadReader (o (n c)) m, Monad n) => m (n (o c))
genericFlip = do
  onc <- ask
  return $ sequence onc

添加其他MonadReader约束不会有任何危害,但是我无法确切地看到您要使用这些monad或其环境实现的目标。

genericFlip ::
  (Traversable o, MonadReader (o (n c)) m, MonadReader a n, MonadReader b o) => m (n (o c))
genericFlip = asks sequence

一个例子可能会有所帮助。