该Applicative
类型类代表松懈monoidal函子是保留对输入功能类别的笛卡尔monoidal结构。
换句话说,给定的规范同构见证(,)
形成一个单面结构:
-- Implementations left to the motivated reader
assoc_fwd :: ((a, b), c) -> (a, (b, c))
assoc_bwd :: (a, (b, c)) -> ((a, b), c)
lunit_fwd :: ((), a) -> a
lunit_bwd :: a -> ((), a)
runit_fwd :: (a, ()) -> a
runit_bwd :: a -> (a, ())
类型类及其定律可以等效地编写为:
class Functor f => Applicative f
where
zip :: (f a, f b) -> f (a, b)
husk :: () -> f ()
-- Laws:
-- assoc_fwd >>> bimap id zip >>> zip
-- =
-- bimap zip id >>> zip >>> fmap assoc_fwd
-- lunit_fwd
-- =
-- bimap husk id >>> zip >>> fmap lunit_fwd
-- runit_fwd
-- =
-- bimap id husk >>> zip >>> fmap runit_fwd
有人可能想知道,对于同一结构,呈单极单调的函子可能是什么样的:
class Functor f => OpApplicative f
where
unzip :: f (a, b) -> (f a, f b)
unhusk :: f () -> ()
-- Laws:
-- assoc_bwd <<< bimap id unzip <<< unzip
-- =
-- bimap unzip id <<< unzip <<< fmap assoc_bwd
-- lunit_bwd
-- =
-- bimap unhusk id <<< unzip <<< fmap lunit_bwd
-- runit_bwd
-- =
-- bimap id unhusk <<< unzip <<< fmap runit_bwd
如果我们考虑一下定义和法律所涉及的类型,就会发现令人失望的事实。OpApplicative
没有比Functor
以下更具体的约束:
instance Functor f => OpApplicative f
where
unzip fab = (fst <$> fab, snd <$> fab)
unhusk = const ()
但是,尽管每个Applicative
函子(实际上Functor
是任何一个)都是微不足道的OpApplicative
,但松驰度Applicative
和OpApplicative
奥普拉斯度之间不一定存在良好的关系。因此,我们可以使用笛卡尔单曲面结构来寻找强单曲面仿函数:
class (Applicative f, OpApplicative f) => StrongApplicative f
-- Laws:
-- unhusk . husk = id
-- husk . unhusk = id
-- zip . unzip = id
-- unzip . zip = id
上面的第一定律是微不足道的,因为该类型的唯一居民() -> ()
是上的恒等函数()
。
但是,其余的三个定律,以及子类本身,也不是小事。具体来说,并非每个Applicative
都是此类的合法实例。
以下是一些Applicative
函子,我们可以为它们声明合法的实例StrongApplicative
:
Identity
VoidF
(->) r
(查看答案)Monoid m => (,) m
Vec (n :: Nat)
Stream
(无限的)
以下是一些Applicative
我们无法做到的:
[]
Either e
Maybe
NonEmptyList
这里的模式表明,StrongApplicative
在某种意义上,阶级是FixedSize
阶级,其中“固定大小” *表示的居民中的居民的多样性**是固定的。a
f a
这可以说是两个推测:
- 每个
Applicative
代表其类型实参元素的“固定大小”容器的实例都是StrongApplicative
- 不
StrongApplicative
存在发生次数a
可以变化的实例
谁能想到反驳这些猜想的反例,或一些令人信服的论证,证明它们是真还是假?
*我意识到我没有正确定义形容词“固定大小”。不幸的是,这项任务有点循环。我不知道“固定大小”容器的任何正式描述,并且正在尝试提出一个。StrongApplicative
是迄今为止我最好的尝试。
为了评估这是否是一个好的定义,我需要进行一些比较。给定某种形式的/非正式的定义,对于一个函子,对于其类型参数的居民而言,具有给定的大小或多重性意味着什么,问题是StrongApplicative
实例的存在是否精确地区分了固定大小和变化大小的函子。
由于不了解现有的正式定义,因此我在使用“固定大小”一词时要求直觉。但是,如果有人已经知道函子大小的现有形式主义并且可以与之进行比较StrongApplicative
,那就更好了。
**“多重性”在广义上是指函子的共域类型的居民中出现了多少个函子的参数类型的任意元素。这是没有考虑到函子被施加到特定类型的,因此不考虑参数类型的任何特定居民。
对此不够精确,导致评论有些混乱,因此以下是一些我认为各种函子的大小/多重性的示例:
VoidF
固定0Identity
固定1Maybe
:变量,最小0,最大1[]
:变量,最小0,最大无限NonEmptyList
:变量,最小1,最大无限Stream
:固定,无限Monoid m => (,) m
固定1data Pair a = Pair a a
固定2Either x
:变量,最小0,最大1data Strange a = L a | R a
固定1
(->) r
is是正确的。
(->) r
; 您需要同构的组件来保留强大的应用结构。出于某种原因Representable
,Haskell中的类型类具有一个神秘的tabulate . return = return
定律(对于非二元函子甚至没有意义),但它为我们提供了我们需要说的条件的1/4,tabulate
并且zip
是适合的类半定形词的态射。其他3条是您必须要求的额外法律。
tabulate
并且index
是适当类别的
return
并不是一个严重的问题。cotraverse getConst . Const
是一个默认的实现return
/ pure
来讲Distributive
,并自distributives / representables有固定的形状,即实现是独一无二的。