Haskell 具有不同单体结构的Lax单体函子

Haskell 具有不同单体结构的Lax单体函子,haskell,functor,applicative,category-theory,alternative-functor,Haskell,Functor,Applicative,Category Theory,Alternative Functor,应用函子因其在有效上下文中应用函数的能力而在Haskeller中广为人知并深受喜爱 从范畴论的角度来看,的方法是实用的: pure :: a -> f a (<*>) :: f (a -> b) -> f a -> f b 其思想是,要编写纯只需将单元中的()替换为给定值,要编写()只需将函数和参数压缩成一个元组,然后在元组上映射合适的应用程序函数 此外,这种对应关系将应用的定律转化为关于单位和(**)的自然单倍体ish定律,因此事实上,应用函子正是范畴理论

应用函子因其在有效上下文中应用函数的能力而在Haskeller中广为人知并深受喜爱

从范畴论的角度来看,
的方法是实用的

pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
其思想是,要编写
只需将
单元
中的
()
替换为给定值,要编写
()
只需将函数和参数压缩成一个元组,然后在元组上映射合适的应用程序函数

此外,这种对应关系将
应用的
定律转化为关于
单位
(**)
的自然单倍体ish定律,因此事实上,应用函子正是范畴理论家所称的松弛单倍体函子(松弛,因为
(**)
仅仅是一种自然变换,而不是同构)

好的,很好,很好。这是众所周知的。但这只是一个松弛幺半函子族——它们尊重乘积的幺半结构。lax幺半函子在源函数和目标函数中包含两种幺半结构选择:如果将乘积转化为和,则得到如下结果:

class PtS f where
  unit :: f Void
  (**) :: f a -> f b -> f (Either a b)

-- some example instances
instance PtS Maybe where
  unit = Nothing
  Nothing ** Nothing = Nothing
  Just a ** Nothing = Just (Left a)
  Nothing ** Just b = Just (Right b)
  Just a ** Just b = Just (Left a) -- ick, but it does satisfy the laws

instance PtS [] where
  unit = []
  xs ** ys = map Left xs ++ map Right ys
由于
unit::Void->f Void
是唯一确定的,所以将求和转换为其他的幺半群结构似乎变得不那么有趣,所以实际上有更多的半群在进行。但仍然:

  • 像上面这样的lax幺半函子是研究过的还是有用的
  • 是否有像
    Applicative
    这样的简洁的替代演示文稿

在讨论幺半体函子之前,您需要确保自己处于。恰好,Hask是一个单倍体范畴,其方式如下:

  • ()
    作为标识
  • (,)
    作为bifunctor
  • 识别同构类型,即
    (a,())≅ ((),a)≅ a
    (a,(b,c))≅ ((a,b),c)
正如您所观察到的,当您将
()
交换为
Void
(,)
交换为
时,它也是一个幺半类
然而,单像线并不能让你走得很远,Hask之所以如此强大,是因为它是。这就给了我们咖喱和相关的技术,没有这些技术,应用程序将毫无用处

当一个单倍体范畴的恒等式是A时,它可以是笛卡尔闭的,即一个存在一个正的类型(当然,我们忽略它)⟂ 这里)箭头。对于任何类型的
A
,都有一个函数
A->()
,即
const()
。但是,没有函数
A->Void
。相反,
Void
是初始对象:它正好存在一个箭头,即
荒谬的::Void->a
方法。这样的单倍体范畴在当时是不能笛卡尔封闭的。
当然,现在你可以通过旋转箭头方向在初始和终端之间轻松切换。这总是把你置于双重结构中,所以我们得到一个。但这意味着您还需要翻转单倍体函子中的箭头。这些被称为(并概括为comonads)。有了Conor令人惊叹的命名方案

class (Functor f) => Decisive f where
  nogood :: f Void -> Void
  orwell :: f (Either s t) -> Either (f s) (f t)

我在范畴理论方面的背景非常有限,但你们的PtS课程FWIW让我想起了,基本上是这样的:

class Applicative f => Alternative f where
  empty :: f a
  (<|>) :: f a -> f a -> f a
class Applicative f=>备选方案f其中
空::f a
()::fa->fa->fa
当然唯一的问题是,
Alternative
Applicative
的扩展。然而,也许有人可以想象它是单独呈现的,与
Applicative
的组合让人想起一个具有非交换环状结构的函子,两个幺半群结构作为环的运算?
Applicative
Alternative
IIRC之间也存在分配规律。

Applicative
的“整洁替代表示”基于以下两个等价关系

pure a = fmap (const a) unit
unit = pure ()

ff <*> fa = fmap (\(f,a) -> f a) $ ff ** fa
fa ** fb = pure (,) <*> fa <*> fb
替换为
,我们可以将类型
()
和构造函数
():()
替换为以恢复
单元

pure :: a  -> f a
pure    () :: f ()
同样地(虽然不是那么简单)将类型
(a,b)
和构造函数
(,)::a->b->(a,b)
替换为
liftA2
,以恢复
**

liftA2 :: (a -> b -> c) -> f a -> f b -> f c
liftA2    (,)           :: f a -> f b -> f (a,b)
Applicative
然后通过将函数application
($)::(a->b)->a->b
提升到函子中来获取nice
操作符

(<*>) :: f (a -> b) -> f a -> f b
(<*>) = liftA2 ($)
操作人员 我们想找到
(**)
的替代方案。除了像
这样的总和之外,还有一种替代方法,可以将它们作为乘积的函数写入。在不存在求和的面向对象编程语言中,它显示为访问者模式

data Either a b = Left a | Right b

{-# LANGUAGE RankNTypes #-}
type Sum a b = forall c. (a -> c) -> (b -> c) -> c
如果您更改了
的参数的顺序并部分应用它们,您将得到这样的结果

either :: (a -> c) -> (b -> c) -> Either a b -> c

toSum :: Either a b -> Sum a b
toSum e = \forA forB -> either forA forB e

toEither :: Sum a b -> Either a b
toEither s = s Left Right
我们可以看到,
a或b≅ 求和a b
。这允许我们重写
(**)

现在很清楚
**
的作用。它延迟
fmap
在它的两个参数上添加某些内容,并将这两个映射的结果组合在一起。如果我们引入一个新操作符,
::fc->fc->fc
,它简单地假设
fmap
已经完成,那么我们可以看到

fmap (\f -> f forA forB) (fa ** fb) = fmap forA fa <||> fmap forB fb
因此,我们可以用下面的类来表示
PtS
可以表示的所有内容,并且可以实现
PtS
的所有内容都可以实现下面的类:

class Functor f => AlmostAlternative f where
    empty  :: f a
    (<||>) :: f a -> f a -> f a

所有a的
。幺半群(fa)
约束是伪码;我不知道如何在Haskell中表达这样的约束。

我知道这并不能真正回答您的问题。在某种程度上,一个幺半函子WRT余积可能仍然很有趣,但我想像
unit
这样的麻烦就像你说的那样微不足道,这在很大程度上阻碍了这一点。我知道你必须这样做
either :: (a -> c) -> (b -> c) -> Either a b -> c

toSum :: Either a b -> Sum a b
toSum e = \forA forB -> either forA forB e

toEither :: Sum a b -> Either a b
toEither s = s Left Right
(**) :: f a -> f b -> f (Either a b)
(**) :: f a -> f b -> f (Sum a b)
(**) :: f a -> f b -> f ((a -> c) -> (b -> c) -> c)
fmap (\f -> f forA forB) (fa ** fb) = fmap forA fa <||> fmap forB fb
fa ** fb = fmap Left fa <||> fmap Right fb
fa1 <||> fa2 = fmap (either id id) $ fa1 ** fa2
class Functor f => AlmostAlternative f where
    empty  :: f a
    (<||>) :: f a -> f a -> f a
class (Functor f, forall a. Monoid (f a)) => MonoidalFunctor f