我有代码(实际上是在C#中,但这个问题与C#无关,所以我会谈到我在Haskell中所说的所有类型),我在Either a b
内工作。然后我bind
一个带有签名的函数,在Haskell中说的是b -> (c, d)
,之后我想将c
拉到外面并在左边的情况下默认它,即我想要{ {1}}。现在这种模式多次出现在我写的一个特定服务中,所以我提出了一种方法来实现它。然而,无论什么时候,我都会困扰我#34;这样的方法没有理解正确的理论基础。换句话说,我们在这里处理什么抽象?
我在一些F#代码中遇到了类似的情况,其中我和我的任何一对都被颠倒了:(c, Either a d)
。我问朋友这是什么,他让我转向traverse,这使我非常高兴,尽管由于缺乏类型类,我必须在F#中进行可怕的单态实现。 (我希望我可以将Visual Studio中的 F1 重新映射到Hackage;它是我编写.NET代码的主要资源之一)。但问题是遍历是:
(a, b) -> (b -> Either c d) -> Either c (a, d)
这意味着当你从一对开始想要"绑定"要么是一个,要么不起作用,当你开始使用一个并希望最终得到一对,因为对不是Applicative。
但是我想到了我的第一个案例,那个不是class (Functor t, Foldable t) => Traversable t where
traverse :: Applicative f => (a -> f b) -> t a -> f (t b)
的案例,并且意识到"在左边案例中默认traverse
"可以通过在左边的情况下进行映射来完成,这会将问题更改为具有此形状:c
我认为这是我们在算术中使用乘法和加法看到的模式:Either (c, a) (c, d) -> (c, Either a d)
。我还记得布尔代数和集合理论中存在相同的模式(如果内存服务,a(b + c) = ab + ac
)。显然,这里有一些抽象的代数结构。但是,内存不起作用,我不记得它被称为什么。维基百科上的一点点问题迅速解决了这个问题:这些是distributive法律。快乐,快乐,凯美特给了我们distribute:
A intersect (B union C) = (A intersect B) union (A intersect C)
它甚至有一个class Functor g => Distributive g where
distribute :: Functor f => f (g a) -> g (f a)
,因为它与cotraverse
是双重的!可爱!!但是,我注意到没有Travsersable
实例。哦,哦。因为,是的,"默认(,)
值"进入这一切?然后我意识到,哦,我可能需要基于bifunctor的投标分配?也许是bitraversable的双重?从概念上讲:
c
这似乎是我所谈论的分配法的结构。我在Haskell找不到这样的东西,因为我实际上在编写C#,所以对我来说并不重要。然而,对我来说重要的是不要提出虚假的抽象,并且尽可能地识别我的代码中的合法抽象,无论它们是否表达,为了我自己的理解< /强>
我目前在我的C#代码中有一个class Bifunctor g => Bidistributive g where
bidistribute :: Bifunctor f => f (g a b) (g a c) -> g a (f b c)
函数(扩展方法)(什么是黑客,对吧!)。我是否完全偏离基础来创建一个(是的,可悲的单形).InsideOut(<default>)
函数(扩展方法)来替换它并映射&#34;默认&#34;左边的情况下,在调用它之前进入左边的情况(或者只是识别&#34;&gt;分配&#34;字符&#34;内部&#34;)
答案 0 :(得分:6)
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
(或者与它同构的东西),而不是任意的bifunctor。那你的功能只是
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)。
答案 1 :(得分:2)
根据@ leftaroundabout的提示来查看附件,除了他在his answer中提到的uncozipL
,如果我们推迟&#34;默认第一个值为左边的那对&#34;,我们也可以用unzipR
来解决这个问题:
unzipR :: Functor u => u (a, b) -> (u a, u b)
然后,仍然需要映射对中的第一个元素,并使用类似either (const "default") id
的值拉出值。有趣的是,如果你使用uncozipL
,你需要知道其中一个是一对。如果您使用unzipR
,则需要知道其中一个是其中之一。在这两种情况下,你都不使用抽象的bifunctor。
此外,我正在寻找的模式或抽象似乎是distributive lattice。维基百科说:
如果L中的所有x,y和z具有以下附加恒等式,则格子(L,∨,∧)是分布的:
x ∧ (y ∨ z) = (x ∧ y) ∨ (x ∧ z).
这正是我在很多不同地方观察到的属性。