【问题标题】:Is there such thing as a bidistributive? What function do I need here?有没有像双向分配这样的东西?我在这里需要什么功能?
【发布时间】:2017-12-22 17:15:42
【问题描述】:

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

我在一些 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)

这意味着当您从一对开始并希望将其“绑定”到它时它会很好,但是当您从一个开始并希望以一对结束时不起作用 ,因为 pair 不是 Applicative

但是我更多地考虑了我的第一个案例,不是traverse,并意识到“在左侧案例中默认c”可以通过映射左侧案例来完成,这会将问题更改为具有这种形状: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))。显然这里有一些抽象的代数结构。然而,记忆不起作用,我不记得它叫什么了。在 Wikipedia 上翻查一下很快就解决了这个问题:这些是 distributive 法律。快乐,噢,快乐,Kmett 给了我们distribute

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

它甚至还有一个cotraverse,因为它是Travsersable 的双重属性!迷人的!!但是,我注意到没有 (,) 实例。哦哦。因为,是的,“默认c 值”在哪里出现?然后我意识到,呃,哦,我可能需要像基于bifunctor 的双向分配器之类的东西?也许是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>) 函数(扩展方法)(真是个 hack,对!)。我会完全偏离基础来创建一个(是的,可悲的是单态的).Bidistribute(...) 函数(扩展方法)来替换它并在调用它之前将左侧案例的“默认”映射到左侧案例(或者只是识别“ “由内而外”的双向分配”字符)?

【问题讨论】:

  • 我猜你会发现Control.Lens.Prism 很有趣。 _Right 棱镜代表一个函数Either a b -> Maybe b 以及一个你并不真正需要的函数b -> Either a b。拥有Either a (c,d) 后,您可以使用_Right . _1 Traversalc 一起使用。

标签: haskell math category-theory bifunctor distributive


【解决方案1】:

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(找到using Hoogle)。

【讨论】:

  • 这是Hask 类别的限制,还是一般类别的限制?
  • @KeithPinson 这是任何具有初始对象的类别的限制。
  • 当然,是Bifunctor (Biconst c)
  • 你的Biconst 需要三个参数。
  • 这是一种表达方式。尽管这并不意味着它“不是分类的”。它仍然很通用,只是特定于副产品双函子。
【解决方案2】:

根据@leftaroundabout 关于查看附加词的提示,除了他在his answer 中提到的uncozipL,如果我们推迟“在任何一个的左侧情况下默认该对的第一个值”,我们也可以用unzipR解决这个问题:

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

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

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

如果以下附加恒等式对 L 中的所有 x、y 和 z 都成立,则格 (L,∨,∧) 是可分配的:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-07-30
    • 2014-09-29
    • 1970-01-01
    • 1970-01-01
    • 2023-03-05
    • 1970-01-01
    • 1970-01-01
    • 2018-06-18
    相关资源
    最近更新 更多