有双向分配这样的东西吗?我在这里需要什么功能?

Is there such thing as a bidistributive? What function do I need here?

我有代码(实际上是在 C# 中,但这个问题与 C# 无关,所以我将在 Haskell-speak 中谈论我的所有类型)我在 Either a b。然后我 bind 一个带有签名的函数,在 Haskell-speak 中是 b -> (c, d),之后我想将 c 拉到外面并在左边的情况下默认它,即我想要 (c, Either a d)。现在,这种模式在我编写的一项特定服务中多次出现,因此我提出了一种方法来实现它。然而,每当我只是 "make up" 一个这样的方法而不理解正确的理论基础时,它都会困扰我。换句话说,我们在这里处理的是什么抽象?

我在某些 F# 代码中遇到了类似的情况,其中我的一对和我的一对被颠倒了:(a, b) -> (b -> Either c d) -> Either c (a, d)。我问一个朋友这是什么,他让我转向 traverse 这让我非常高兴,尽管由于缺少类型类,我不得不在 F# 中进行可怕的单态实现。 (我希望我可以将 Visual Studio 中的 F1 重新映射到 Hackage;它是我编写 .NET 代码的主要资源之一)。但问题是遍历是:

class (Functor t, Foldable t) => Traversable t where
    traverse :: Applicative f => (a -> f b) -> t a -> f (t b)

这意味着当您从一对开始并想 "bind" 一个对它时,它工作得很好,但是 不起作用 当您从一个和想以一对结束,因为一对不是 Applicative.

但是我更多地考虑了我的第一个案例,那个不是 traverse 的案例,并意识到 "defaulting c in the left case" 可以通过映射左侧案例来完成,这将问题变成了这个形状:Either (c, a) (c, d) -> (c, Either a d),我认为这是我们在乘法和加法算术中看到的模式:a(b + c) = ab + ac。我还记得布尔代数和集合论中存在相同的模式(如果没记错,A intersect (B union C) = (A intersect B) union (A intersect C))。显然这里有一些抽象的代数结构。但是,记忆不起作用,我记不起它叫什么了。在维基百科上稍作探索很快就解决了这个问题:这些是 distributive laws. And joy, oh joy, Kmett has given us distribute:

class Functor g => Distributive g where
    distribute :: Functor f => f (g a) -> g (f a)

它甚至还有一个 cotraverse 因为它是 Travsersable 的对偶!迷人的!!但是,我注意到没有 (,) 实例。呃哦。因为,是的,"default c value" 是从哪里来的?然后我意识到,哦,哦,我也许需要类似基于 bifunctor? perhaps dual to bitraversable 的双向分配?概念上:

class Bifunctor g => Bidistributive g where
    bidistribute :: Bifunctor f => f (g a b) (g a c) -> g a (f b c)

这好像就是我说的分配律的结构。我在 Haskell 中找不到这样的东西,这对我来说本身并不重要,因为我实际上是在编写 C#。然而,对我来说重要的是不要提出虚假的抽象,并且要在我的代码中识别尽可能多的合法抽象,无论它们是否这样表达,为了我自己理解.

我目前在我的 C# 代码中有一个 .InsideOut(<default>) 函数(扩展方法)(多么骇人听闻,对吧!)。我是否会完全偏离基础来创建一个(是的,遗憾的是单态的).Bidistribute(...) 函数(扩展方法)来替换它并在调用它之前将左侧案例的 "default" 映射到左侧案例(或者只需识别 "inside out")?

的 "bidistributive" 字符

bidistribute 不能这样实现。考虑这个简单的例子

data Biconst c a b = Biconst c

instance Bifunctor (Biconst c) where
  bimap _ _ (Biconst c) = Biconst c

那我们就专精了

bidistribute :: Biconst () (Void, ()) (Void, ()) -> (Void, Biconst () () ())
bidistribute (Biconst ()) = ( ????, Biconst () )

显然没有办法填补空白,需要输入 Void.

实际上,我认为您确实需要 Either 那里(或与之同构的东西)而不是任意的双函子。那么你的函数就是

uncozipL :: Functor f => Either (f a) (f b) -> f (Either a b)
uncozipL (Left l) = Left <$> l
uncozipL (Right r) = Right <$> l

已定义 in adjunctions (found using Hoogle)。

根据@leftaroundabout的tip-off看adjunctions,除了他在中提到的uncozipL,如果我们推迟"default the first value of the pair in the left case of either",我们也可以解决这个问题unzipR:

unzipR :: Functor u => u (a, b) -> (u a, u b)

然后仍然需要映射对中的第一个元素,并用 either (const "default") id 之类的东西提取值。有趣的是,如果你使用 uncozipL,你需要知道其中一个是一对。如果使用 unzipR,则需要知道 one 是 either。在这两种情况下,您都不使用抽象双函子。

此外,我正在寻找的模式或抽象似乎是 distributive lattice。维基百科说:

A lattice (L,∨,∧) is distributive if the following additional identity holds for all x, y, and z in L:

x ∧ (y ∨ z) = (x ∧ y) ∨ (x ∧ z).

这正是我在许多不同地方观察到的 属性。