简体   繁体   English

在什么程度上唯一确定了Applicative / Monad实例?

[英]To what extent are Applicative/Monad instances uniquely determined?

As described this question/answers , Functor instances are uniquely determined, if they exists. 本问题所述, Functor实例是唯一确定的(如果存在)。

For lists, there are two well know Applicative instances: [] and ZipList . 对于列表,有两个众所周知的Applicative实例: []ZipList So Applicative isn't unique (see also Can GHC derive Functor and Applicative instances for a monad transformer? and Why is there no -XDeriveApplicative extension? ). 因此, Applicative不是唯一的 (另请参见GHC可以为monad转换器派生Functor和Applicative实例吗? 为什么没有-XDeriveApplicative扩展? )。 However, ZipList needs infinite lists, as its pure repeats a given element indefinitely. 但是, ZipList需要无限列表,因为其pure无限期地重复给定元素。

  • Are there other, perhaps better examples of data structures that have at least two Applicative instances? 是否还有其他至少具有两个Applicative实例的数据结构示例,也许是更好的示例?
  • Are there any such examples that only involve finite data structures? 是否有仅涉及有限数据结构的此类示例? That is, like if hypothetically Haskell's type system distinguished inductive and coinductive data types, would it be possible to uniquely determine Applicative? 也就是说,就像假设的Haskell类型系统区分归纳和共数据类型一样,是否有可能唯一地确定Applicative?

Going further, if we could extend both [] and ZipList to a Monad, we'd have an example where a monad isn't uniquely determined by the data type and its Functor. 更进一步,如果我们可以将[]ZipList都扩展为Monad,我们将有一个示例,其中monad不是由数据类型及其Functor唯一地确定的。 Alas, ZipList has a Monad instance only if we restrict ourselves to infinite lists ( streams ). ZipList 只有当我们将自己限制在无限列表 )中时, ZipList才具有Monad实例。 And return for [] creates a single-element list, so it requires finite lists. return []会创建一个单元素列表,因此它需要有限列表。 Therefore: 因此:

  • Are Monad instances uniquely determined by the data type? Monad实例是否由数据类型唯一确定? Or is there an example of a data type that can have two distinct Monad instances? 还是有一个可以具有两个不同的Monad实例的数据类型示例?

In the case there is an example with two or more distinct instances, an obvious question arises, if they must/can have the same Applicative instance: 在有两个或多个不同实例的示例的情况下,如果它们必须/可以具有相同的Applicative实例,就会出现一个明显的问题:

  • Are Monad instances uniquely determined by the Applicative instance, or is there an example of an Applicative that can have two distinct Monad instances? Monad实例是由Applicative实例唯一确定的,还是存在可以具有两个不同Monad实例的Applicative的示例?
  • Is there an example of a data type with two distinct Monad instances, each having a different Applicative super-instance? 是否有一个数据类型的示例,其中包含两个不同的Monad实例,每个实例都有一个不同的Applicative超级实例?

And finally we can ask the same question for Alternative/MonadPlus. 最后,我们可以对Alternative / MonadPlus提出相同的问题。 This is complicated by the fact that there are two distinct set of MonadPlus laws . 由于存在两种不同的MonadPlus法则 ,这使情况变得复杂。 Assuming we accept one of the set of laws (and for Applicative we accept right/left distributivity/absorption , see also this question ), 假设我们接受其中一组定律(对于应用,我们接受右/左分布/吸收 ,另请参见此问题 ),

  • is Alternative uniquely determined by Applicative, and MonadPlus by Monad, or are there any counter-examples? “替代方案”是由Applicative唯一确定的,还是MonadPlus由Monad唯一确定,还是有任何反例?

If any of the above are unique, I'd be interested in knowing why, to have a hint of a proof. 如果以上任何一个都不是唯一的,我很想知道为什么,以得到一个提示。 If not, an counter-example. 如果不是,则为反例。

First, since Monoid s are not unique, neither are Writer Monad s or Applicative s. 首先,由于Monoid不是唯一的,所以Writer MonadApplicative都不一样。 Consider 考虑

data M a = M Int a

then you can give it Applicative and Monad instances isomorphic to either of: 那么您可以将ApplicativeMonad实例同构为以下任意一个:

Writer (Sum Int)
Writer (Product Int)

Given a Monoid instance for a type s , another isomorphic pair with different Applicative / Monad instances is: 给定类型sMonoid实例,另一个具有不同Applicative / Monad实例的同构对是:

ReaderT s (Writer s)
State s

As for having one Applicative instance extend to two different Monad s, I cannot remember any example. 至于将一个Applicative实例扩展到两个不同的Monad ,我不记得任何示例。 However, back when I tried to convince myself completely about whether ZipList really cannot be made a Monad , I found the following pretty strong restriction that holds for any Monad : 但是,当我试图完全说服ZipList是否真的不能成为Monad ,我发现以下对Monad严格限制:

join (fmap (\x -> fmap (\y -> f x y) ys) xs) = f <$> xs <*> ys

That doesn't give join for all values though: in the case of lists the restricted values are the ones where all elements have the same length, ie lists of lists with "rectangular" shape. 但是,这并不能为所有值提供join :对于列表,限制值是所有元素都具有相同长度的值,即具有“矩形”形状的列表的列表。

(For Reader monads, where the "shape" of monadic values doesn't vary, these are in fact all the m (mx) values, so those do have unique extension. EDIT: Come to think of it, Either , Maybe and Writer also have only "rectangular" m (mx) values, so their extension from Applicative to Monad is also unique.) (对于Reader monad,其monadic值的“形状”没有变化,实际上它们都是m (mx)值,因此它们确实具有唯一的扩展名。编辑:来思考一下, EitherMaybeWriter也只有“矩形” m (mx)值,因此它们从ApplicativeMonad的扩展名也是唯一的。)

I wouldn't be surprised if an Applicative with two Monad s exists, though. 但是,如果存在带有两个MonadApplicative ,我不会感到惊讶。

For Alternative / MonadPlus , I cannot recall any law for instances using the Left Distribution law instead of Left Catch, I see nothing preventing you from just swapping (<|>) with flip (<|>) . 对于Alternative / MonadPlus ,对于使用“左分布”定律而不是“左捕获” 定律的实例, 我无法回忆起任何定律 ,我看不出有什么可以阻止您仅将(<|>)flip (<|>)交换。 I don't know if there's a less trivial variation. 我不知道是否有比较小的变化。

ADDENDUM: I suddenly remembered I had found an example of an Applicative with two Monad s. 附录:我突然想起我找到一个示例Applicative有两个Monad秒。 Namely, finite lists. 即,有限列表。 There's the usual Monad [] instance, but you can then replace its join by the following function (essentially making empty lists "infectious"): 这是通常的Monad []实例,但是您可以用以下函数替换其join (基本上使空列表具有“感染性”):

ljoin xs
  | any null xs = []
  | otherwise   = concat xs

(Alas, the lists need to be finite because otherwise the null check will never finish, and that would ruin the join . fmap return == id monad law.) (A,列表必须是有限的,因为否则null检查将永远不会完成,并且会破坏join . fmap return == id monad law。)

This has the same value as join / concat on rectangular lists of lists, so will give the same Applicative . 它与矩形列表中的join / concat具有相同的值,因此具有相同的Applicative As I recall, it turns out that the first two monad laws are automatic from that, and you just need to check ljoin . ljoin == ljoin . fmap ljoin 我记得,事实证明,前两个monad定律是自动产生的,您只需要检查ljoin . ljoin == ljoin . fmap ljoin ljoin . ljoin == ljoin . fmap ljoin ljoin . ljoin == ljoin . fmap ljoin . ljoin . ljoin == ljoin . fmap ljoin

Given that every Applicative has a Backwards counterpart, 鉴于每个Applicative都有一个Backwards对应项,

newtype Backwards f x = Backwards {backwards :: f x}
instance Applicative f => Applicative (Backwards f) where
  pure x = Backwards (pure x)
  Backwards ff <*> Backwards fs = Backwards (flip ($) <$> fs <*> ff)

it's unusual for Applicative to be uniquely determined, just as (and this is very far from unrelated) many sets extend to monoids in multiple ways. Applicative唯一地确定是不寻常的,就像许多集合以多种方式扩展到类半体一样(这是很不相关的)。

In this answer , I set the exercise of finding at least four distinct valid Applicative instances for nonempty lists: I won't spoil it here, but I will give a big hint on how to hunt. 此答案中 ,我将练习为非空列表至少查找四个不同的有效Applicative实例:我不会在这里宠坏它,但会为如何狩猎提供很大的提示。

Meanwhile, in some wonderful recent work (which I saw at a summer school a few months ago), Tarmo Uustalu showed a rather neat way to get a handle on this problem, at least when the underlying functor is a container , in the sense of Abbott, Altenkirch and Ghani. 同时,在最近的一些出色的工作中(我几个月前在一家暑期学校看到的),Tarmo Uustalu展示了一种解决此问题的巧妙方法,至少在底层函子是容器的情况下 ,雅培,阿尔滕基希和加尼。

Warning: Dependent types ahead! 警告:前面的依赖类型!

What is a container? 什么是容器? If you have dependent types to hand, you can present container-like functors F uniformly, as being determined by two components 如果您要处理依赖类型,则可以由两个组件确定,均匀地呈现类似容器的函子F

  1. a set of shapes, S : Set 一组形状,S:集合
  2. an S-indexed set of positions, P : S -> Set 一个S索引的位置集,P:S-> Set

Up to isomorphism, container data structures in FX are given by the dependent pair of some shape s : S, and some function e : P s -> X, which tells you the element located at each position. 直到同构为止,FX中的容器数据结构由某些形状s:S和一些函数e:P s-> X的相关对提供,这些告诉您位于每个位置的元素。 That is, we define the extension of a container 也就是说,我们定义了容器的扩展名

(S <| P) X = (s : S) * (P s -> X)

(which, by the way, looks a lot like a generalized power series if you read -> as reversed exponentiation). (顺便说一句,如果您将->读为反幂,它看起来很像广义幂级数)。 The triangle is supposed to remind you of a tree node sideways, with an element s : S labelling the apex, and the baseline representing the position set P s. 三角形应该让您想起树形节点的侧面,元素s:S标记顶点,基线表示位置集P s。 We say that some functor is a container if it is isomorphic to some S <| P 我们说某些函子是容器,如果它与某些S <| P同构S <| P S <| P . S <| P

In Haskell, you can easily take S = F () , but constructing P can take quite a bit of type-hackery. 在Haskell中,您可以轻松地采用S = F () ,但是构造P可能会占用很多类型黑客。 But that is something you can try at home. 但这您可以在家尝试的方法。 You'll find that containers are closed under all the usual polynomial type-forming operations, as well as identity, 您会发现容器在所有常见的多项式类型形成操作以及标识,

Id ~= () <| \ _ -> ()

composition, where a whole shape is made from just one outer shape and an inner shape for each outer position, 构图,其中整个形状仅由一个外部形状和每个外部位置的内部形状组成,

(S0 <| P0) . (S1 <| P1)  ~=  ((S0 <| P0) S1) <| \ (s0, e0) -> (p0 : P0, P1 (e0 p0))

and some other things, notably the tensor , where there is one outer and one inner shape (so "outer" and "inner" are interchangeable) 还有其他一些东西,特别是张量 ,那里有一个外部形状和一个内部形状(因此“外部”和“内部”可以互换)

(S0 <| P0) (X) (S1 <| P1)   =   ((S0, S1) <| \ (s0, s1) -> (P0 s0, P1 s1))

so that F (X) G means " F -structures of G -structures-all-the-same-shape", eg, [] (X) [] means rectangular lists-of-lists. 因此, F (X) G表示“ G结构的F结构-相同形状”,例如[] (X) []表示矩形列表。 But I digress 但是我离题

Polymorphic functions between containers Every polymorphic function 容器之间的多态函数每个多态函数

m : forall X. (S0 <| P0) X -> (S1 <| P1) X

can be implemented by a container morphism , constructed from two components in a very particular way. 可以通过容器形态来实现, 容器形态由两个组件以非常特殊的方式构造。

  1. a function f : S0 -> S1 mapping input shapes to output shapes; 函数f : S0 -> S1将输入形状映射到输出形状;
  2. a function g : (s0 : S0) -> P1 (f s0) -> P0 s0 mapping output positions to input positions. 函数g : (s0 : S0) -> P1 (f s0) -> P0 s0将输出位置映射到输入位置。

Our polymorphic function is then 那么我们的多态函数是

\ (s0, e0) -> (f s0, e0 . g s0)

where the output shape is computed from the input shape, then the output positions are filled up by picking elements from input positions. 其中从输入形状计算出输出形状,然后通过从输入位置拾取元素来填充输出位置。

(If you're Peter Hancock, you have a whole other metaphor for what's going on. Shapes are Commands; Positions are Responses; a container morphism is a device driver , translating commands one way, then responses the other.) (如果您是Peter Hancock,那么您对正在发生的事情有一个完全的比喻。形状是命令;位置是响应;容器形态是设备驱动程序 ,一种方式翻译命令,然后另一种方式响应。)

Every container morphism gives you a polymorphic function, but the reverse is also true. 每个容器的态素都为您提供了一个多态函数,但反之亦然。 Given such an m, we may take 给定这样一个m,我们可能会

(f s, g s) = m (s, id)

That is, we have a representation theorem , saying that every polymorphic function between two containers is given by such an f , g -pair. 也就是说,我们有一个表示定理 ,说两个容器之间的每个多态函数都由这样一个fg对给出。

What about Applicative ? 那么Applicative呢? We kind of got a bit lost along the way, building all this machinery. 在构建所有这些机器的过程中,我们有点迷路了。 But it has been worth it. 是值得的。 When the underlying functors for monads and applicatives are containers, the polymorphic functions pure and <*> , return and join must be representable by the relevant notion of container morphism. 当monads和appadatives的基础函子为容器时,多态函数pure<*>returnjoin必须由容器态射的相关概念表示。

Let's take applicatives first, using their monoidal presentation. 首先,让我们使用应用程序的半形形式。 We need 我们需要

unit : () -> (S <| P) ()
mult : forall X, Y. ((S <| P) X, (S <| P) Y) -> (S <| P) (X, Y)

The left-to-right maps for shapes require us to deliver 形状的从左到右的地图要求我们提供

unitS : () -> S
multS : (S, S) -> S

so it looks like we might need a monoid. 所以看起来我们可能需要一个半身像。 And when you check that the applicative laws, you find we need exactly a monoid. 当您检查适用法律时,您会发现我们恰恰需要一个半同等式。 Equipping a container with applicative structure is exactly refining the monoid structures on its shapes with suitable position-respecting operations. 为容器配备可应用的结构是通过适当的位置尊重操作来精确地将单面体结构细化为其形状。 There's nothing to do for unit (because there is no chocie of source position), but for mult , we need that whenenver unit无关(因为没有源位置的选择),但是对于mult ,我们需要在

multS (s0, s1) = s

we have 我们有

multP (s0, s1) : P s -> (P s0, P s1)

satisfying appropriate identity and associativity conditions. 满足适当的身份和关联性条件。 If we switch to Hancock's interpretation, we're defining a monoid (skip, semicolon) for commands, where there is no way to look at the response to the first command before choosing the second, like commands are a deck of punch cards. 如果切换到汉考克的解释,我们将为命令定义一个等分线(跳过,分号),在选择第二个命令之前,无法查看对第一个命令的响应,就像命令是一副打孔卡一样。 We have to be able to chop up responses to combined commands into the individual responses to the individual commands. 我们必须能够将对组合命令的响应分成对单个命令的单个响应。

So, every monoid on the shapes gives us a potential applicative structure. 因此,形状上的每个monoid都为我们提供了潜在的应用结构。 For lists, shapes are numbers (lengths), and there are a great many monoids from which to choose. 对于列表,形状是数字(长度),并且有很多可供选择的monoid。 Even if shapes live in Bool , we have quite a bit of choice. 即使形状存在于Bool ,我们也有很多选择。

What about Monad ? Monad呢? Meanwhile, for monads M with M ~= S <| P 同时,对于M〜 M ~= S <| P单子M M ~= S <| P . M ~= S <| P We need 我们需要

return : Id -> M
join   : M . M -> M

Looking at shapes first, that means we need a sort-of lopsided monoid. 首先看形状,这意味着我们需要一种偏斜的类半体。

return_f : () -> S
join_f   : (S <| P) S -> S  --  (s : S, P s -> S) -> S

It's lopsided because we get a bunch of shapes on the right, not just one. 这是不平衡的,因为我们在右侧获得了很多形状,而不仅仅是一个。 If we switch to Hancock's interpretation, we're defining a kind of sequential composition for commands, where we do let the second command be chosen on the basis of the first response, like we're interacting at a teletype. 如果切换到汉考克的解释,我们将为命令定义一种顺序组成,在这种情况下,我们会根据第一个响应选择第二个命令,就像在电传打字机上进行交互一样。 More geometrically, we're explaining how to glom two layers of a tree into one. 从几何角度上讲,我们正在解释如何将树的两层粘合为一层。 It would be very surprising if such compositions were unique. 如果这种组合物是独特的,那将是非常令人惊讶的。

Again, for the positions, we have to map single output positions to pairs in a coherent way. 同样,对于位置,我们必须以连贯的方式将单个输出位置映射为对。 This is trickier for monads: we first choose an outer position (response), then we have to choose an inner position(response) appropriate to the shape (command) found at the first position (chosen after the first response). 这对于单子来说比较棘手:我们首先选择一个外部位置(响应),然后我们必须选择一个适合于在第一个位置(在第一个响应之后选择)的形状(命令)的内部位置(响应)。

I'd love to link to Tarmo's work for the details, but it doesn't seem to have hit the streets yet. 我很想链接到Tarmo的工作中,以获取详细信息,但似乎还没有出现。 He has actually used this analysis to enumerate all possible monad structures for several choices of underlying container. 他实际上已经使用此分析来枚举用于基础容器的几种选择的所有可能的monad结构。 I'm looking forward to the paper! 我期待着这篇论文!

Edit. 编辑。 By way of doing honour to the other answer, I should observe that when everywhere P s = () , then (S <| P) X ~= (S, X) and the monad/applicative structures coincide exactly with each other and with the monoid structures on S . 通过尊重其他答案,我应该观察到,当无处不在P s = () ,则(S <| P) X ~= (S, X)与monad /应用结构彼此完全一致,并且与S上的monoid结构。 That is, for writer monads, we need only choose the shape-level operations, because there is exactly one position for a value in every case. 也就是说,对于编写器monad,我们只需要选择形状级别的操作,因为在每种情况下,值的位置恰好只有一个位置。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM