【发布时间】: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 . _1Traversal与c一起使用。
标签: haskell math category-theory bifunctor distributive