应用函子的逆提升

Inverse lifting on applicative functors

我很确定这有一个简单的解决方案,但它让我望而却步,我似乎找不到直接的答案。

通常,当应用liftA2假设二进制函数已经被解除一次时,签名看起来像这样:

liftA2'   :: (Applicative f1, Applicative f) 
             => (f a -> f b -> f c) -> f1 (f a) -> f1 (f b) -> f1 (f c)

是否可以应用"inverse"的,例如liftA2如:

inverseA2 :: (Applicative f, Applicative f1) 
             => (f a -> f b -> f c) -> f (f1 a) -> f (f1 b) -> f (f1 c)

作为一个具体的例子,我想获得函数:

f :: ([a] -> [b] -> [c]) -> [Maybe a] -> [Maybe b] -> [Maybe c]

一种方法是诉诸 "pack" 每个参数 [Maybe a] -> Maybe [a] 和 "unpack" Maybe [a] -> [Maybe a] 应用正常 liftA2 的结果。我想避免这种情况,因为正如您可以想象的那样,打包具有破坏性(例如 pack [Just 1, Nothing, Just 2] == Nothing )。


更新: 正如@user2407038 指出的那样,为了 f 应用给定的函数,你必须需要一个类似于 [Maybe a] -> [a] 的函数 会丢失信息。因此,对于这两个特定的函子,没有明显的方法来满足所提出的额外要求。但是对于任何其他两个具有可逆函数 forall a . f a -> f1 a 的函子 ff1,接受的答案非常适合作为这个问题的解决方案。

我相信您可能已经明白了这一点,但我认为您无法在现有的限制条件下做到这一点。如果你对自己的限制更自由一点,你会得到一些东西......

inverseA2 :: (Applicative f, Traversable f, Applicative f1, Traversable f1)
               => (f a -> f b -> f c) -> f (f1 a) -> f (f1 b) -> f (f1 c)
inverseA2 f x y = sequenceA (liftA2 f (sequenceA x) (sequenceA y))

我提出这个问题的唯一原因是,对于您使用 Maybe[] 的特定示例,这些约束 都已满足,所以在这种情况下这样做是可能的。不过还是一点都没有安定下来。

您也可以尝试为 Data.Distributive 编写自己的实例,从而得到 distribute,这类似于 sequenceA...

编辑以包含@dfeuer 的建议。