我想写一个带有类型签名的对象:
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 中制作具有此类型签名的总对象?
首先,我建议不要将m
推广到MonadReader
。在flip
的类型...
flip :: (a -> b -> c) -> (b -> a -> c)
。中间的箭头与其他箭头不同:它只是链接翻转的输入和输出。采用这种简化,我们最终为您提出的genericFlip
提供了更简单的类型:
genericFlip :: (MonadReader a n, MonadReader b o) => o (n c) -> n (o c)
在任何情况下,genericFlip
都不能使用此签名实现。就其本身而言,MonadReader
接口不提供为计算提供环境的方法,而计算环境是交换层所必需的。例如,考虑一下您问题中的专业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
,我们可以通过runReader
做同样的事情(。最终,MonadReader
在这里所做的只是将函数转换为读取器计算,正如这个毫无意义的拼写所显示的那样:
genericFlip' :: (MonadReader a n, MonadReader b o) => (b -> a -> c) -> n (o c)
genericFlip' = fmap reader . reader . flip
我们所拥有的flip
的一个概括是distribute
:
distribute :: (Distributive g, Functor f) => f (g a) -> g (f a)
distribute @((->) _)
也被称为flap
或(??)
,而distribute @((->) _) @((->) _)
本身flip
。
然而,Distributive
并没有像我们在这个问题的上下文中希望的那样完全将我们与函数分开。对于某些特定r
,每个分布函子都同构于(->) r
。当我们查看Representable
类时,连接变得更加明显,它原则上等同于Distributive
,但使用更复杂的编码使同构显式。除了distribute
是flip
的推广之外,我们还有index
作为函数应用模拟,以及看起来很像reader
的tabulate
。实际上,该类提供了一个默认的MonadReader
实现,可以通过Co
newtype方便地派生。
最后一点,尽管不完全符合我们提出的广义翻转签名,但非常可翻转的东西是ReaderT r (ReaderT s m) a
,归结为r -> s -> m a
.不过,这可以说并不是那么有用,因为在实践中,人们通常会将环境组合成一个类型,并且只有一个阅读器层,而不是嵌套的读取器层(另请参阅RIO
monad(。
No.这是不可能的。
第一个签名没有任何内容使 o 可遍历。
仅仅依靠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
一个例子可能会有所帮助。