不是函子/函子/应用/单子的好例子?
在向某人解释什么是类型类 X 时,我很难找到恰好是 X 的数据结构的好示例。
因此,我请求以下示例:
- 不是函子的类型构造函数。
- 类型构造函数是 Functor,但不是 Applicative。
- 类型构造函数是 Applicative,但不是 Monad。
- 类型构造函数是 Monad。
我认为到处都有很多 Monad 的例子,但是一个与前面的例子有一定关系的 Monad 的好例子可以完整地描述这一情况。
我寻找彼此相似的示例,仅在属于特定类型类的重要方面有所不同。
如果有人能够设法在这个层次结构中的某个地方(是在 Applicative 和 Monad 之间吗?)偷偷拿一个 Arrow 的例子,那也太好了!
While explaining to someone what a type class X is I struggle to find good examples of data structures which are exactly X.
So, I request examples for:
- A type constructor which is not a Functor.
- A type constructor which is a Functor, but not Applicative.
- A type constructor which is an Applicative, but is not a Monad.
- A type constructor which is a Monad.
I think there are plenty examples of Monad everywhere, but a good example of Monad with some relation to previous examples could complete the picture.
I look for examples which would be similar to each other, differing only in aspects important for belonging to the particular type class.
If one could manage to sneak up an example of Arrow somewhere in this hierarchy (is it between Applicative and Monad?), that would be great too!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
不是函子的类型构造函数:
您可以用它创建逆变函子,但不能创建(协变)函子。尝试编写
fmap
,你会失败。请注意,逆变函子版本是相反的:类型构造函数是函子,但不是 Applicative:
我没有一个很好的例子。有
Const
,但理想情况下我想要一个具体的非 Monoid,但我想不出任何。当你认真思考时,所有类型基本上都是数字、枚举、乘积、求和或函数。您可以在下面看到 Pigworker 和我对于Data.Void
是否是Monoid
存在分歧;由于
_|_
是 Haskell 中的合法值,并且实际上是Data.Void
的唯一合法值,因此这符合 Monoid 规则。我不确定unsafeCoerce
与它有什么关系,因为一旦您使用任何unsafe
函数,您的程序就不再保证不违反 Haskell 语义。请参阅 Haskell Wiki,获取有关底部 (链接) 或不安全函数 (链接)。
我想知道是否可以使用更丰富的类型系统(例如具有各种扩展的 Agda 或 Haskell)创建这样的类型构造函数。
一个类型构造函数,它是一个 Applicative,但不是一个 Monad:
你可以用它来创建一个 Applicative,类似于:
但是如果你把它变成一个 monad,你可能会得到维度不匹配。我怀疑这样的例子在实践中很少见。
作为 Monad 的类型构造函数:
关于箭头:
询问箭头在此层次结构中的位置就像询问“红色”是什么形状。注意类型不匹配:
但是,
A type constructor which is not a Functor:
You can make a contravariant functor out of it, but not a (covariant) functor. Try writing
fmap
and you'll fail. Note that the contravariant functor version is reversed:A type constructor which is a functor, but not Applicative:
I don't have a good example. There is
Const
, but ideally I'd like a concrete non-Monoid and I can't think of any. All types are basically numeric, enumerations, products, sums, or functions when you get down to it. You can see below pigworker and I disagreeing about whetherData.Void
is aMonoid
;Since
_|_
is a legal value in Haskell, and in fact the only legal value ofData.Void
, this meets the Monoid rules. I am unsure whatunsafeCoerce
has to do with it, because your program is no longer guaranteed not to violate Haskell semantics as soon as you use anyunsafe
function.See the Haskell Wiki for an article on bottom (link) or unsafe functions (link).
I wonder if it is possible to create such a type constructor using a richer type system, such as Agda or Haskell with various extensions.
A type constructor which is an Applicative, but not a Monad:
You can make an Applicative out of it, with something like:
But if you make it a monad, you could get a dimension mismatch. I suspect that examples like this are rare in practice.
A type constructor which is a Monad:
About Arrows:
Asking where an Arrow lies on this hierarchy is like asking what kind of shape "red" is. Note the kind mismatch:
but,
我的风格可能会受到手机的限制,但就这样吧。
不能是函子。如果是的话,我们就会拥有
,月球也会由绿色奶酪制成。
同时
是一个函子
,但不能应用,否则我们就会有
,绿色将由月亮奶酪制成(这实际上可能发生,但只能在晚上晚些时候)。
(额外注意:
Void
,如Data.Void
中是一个空数据类型。如果你尝试使用undefined
来证明它是一个Monoid,我'将使用unsafeCoerce
来证明它不是。)令人高兴的是,
它在很多方面都是适用的,例如,正如 Dijkstra 所拥有的那样,
但它不能是 Monad。要了解原因,请观察 return 必须始终为
Boo True
或Boo False
,因此不可能成立。
哦,是的,我差点忘了
这是一个 Monad。自己滚。
飞机去赶...
My style may be cramped by my phone, but here goes.
cannot be a Functor. If it were, we'd have
and the Moon would be made of green cheese.
Meanwhile
is a functor
but cannot be applicative, or we'd have
and Green would be made of Moon cheese (which can actually happen, but only later in the evening).
(Extra note:
Void
, as inData.Void
is an empty datatype. If you try to useundefined
to prove it's a Monoid, I'll useunsafeCoerce
to prove that it isn't.)Joyously,
is applicative in many ways, e.g., as Dijkstra would have it,
but it cannot be a Monad. To see why not, observe that return must be constantly
Boo True
orBoo False
, and hence thatcannot possibly hold.
Oh yeah, I nearly forgot
is a Monad. Roll your own.
Plane to catch...
我相信其他答案错过了一些简单且常见的示例:
类型构造函数是 Functor 但不是 Applicative。一个简单的示例是一对:
但无法定义其
适用的
实例,不对r
施加额外的限制。特别是,没有办法如何定义pure::a -> (r, a)
表示任意r
。类型构造函数是 Applicative,但不是 Monad。 一个众所周知的例子是 ZipList。 (它是一个包装列表并为它们提供不同的 Applicative 实例的 newtype 。)
fmap 以通常的方式定义。但是
pure
和<*>
的定义如下:pure
通过重复给定值创建一个无限列表,并且<* *>
使用值列表压缩函数列表 - 将第 i 个函数应用于第 i 个元素。 ([]
上的标准<*>
生成将第 i 个函数应用于 j 的所有可能组合>-th 元素。)但是没有明智的方法来定义 monad (参见 this帖子)。箭头如何适应函子/应用/单子层次结构?
请参阅习语是不经意的,箭头是细致的,单子是混杂的作者:萨姆·林德利、菲利普·瓦德勒、杰里米·雅洛普。 MSFP 2008。(他们称之为应用函子习语。)摘要:
I believe the other answers missed some simple and common examples:
A type constructor which is a Functor but not an Applicative. A simple example is a pair:
But there is no way how to define its
Applicative
instance without imposing additional restrictions onr
. In particular, there is no way how to definepure :: a -> (r, a)
for an arbitraryr
.A type constructor which is an Applicative, but is not a Monad. A well-known example is ZipList. (It's a
newtype
that wraps lists and provides differentApplicative
instance for them.)fmap
is defined in the usual way. Butpure
and<*>
are defined asso
pure
creates an infinite list by repeating the given value, and<*>
zips a list of functions with a list of values - applies i-th function to i-th element. (The standard<*>
on[]
produces all possible combinations of applying i-th function to j-th element.) But there is no sensible way how to define a monad (see this post).How arrows fit into the functor/applicative/monad hierarchy?
See Idioms are oblivious, arrows are meticulous, monads are promiscuous by Sam Lindley, Philip Wadler, Jeremy Yallop. MSFP 2008. (They call applicative functors idioms.) The abstract:
对于不是函子的类型构造函数来说,
Set
是一个很好的例子:您无法实现fmap :: (a -> b) ->发-> f b
,因为如果没有额外的约束Ord b
,您就无法构造f b
。A good example for a type constructor which is not a functor is
Set
: You can't implementfmap :: (a -> b) -> f a -> f b
, because without an additional constraintOrd b
you can't constructf b
.我想提出一种更系统的方法来回答这个问题,并展示不使用任何特殊技巧(例如“底部”值或无限数据类型或类似的东西)的示例。
什么时候类型构造函数无法拥有类型类实例?
一般来说,类型构造函数无法获得特定类型类的实例有两个原因:
第一种的例子比第二种更容易,因为对于第一种,我们只需要检查是否可以实现具有给定类型签名的函数,而对于第二种,我们需要证明不能实现可能会满足法律。
具体示例
无法拥有函子实例的类型构造函数,因为类型无法实现:
相对于类型参数
a,这是一个反函子,而不是函子
,因为a
处于逆变位置。不可能用类型签名(a -> b) -> 来实现函数。 F za -> F z b 。
类型构造函数不是合法函子,即使可以实现
fmap
的类型签名:这个例子的奇怪之处在于我们可以实现正确类型的
fmap
,即使F
不可能是函子,因为它在a中使用a
逆变位置。因此,上面显示的 fmap 的实现具有误导性 - 尽管它具有正确的类型签名(我相信这是该类型签名的唯一可能的实现),但不满足函子定律。例如,fmap id
≠id
,因为let (Q(f,_)) = fmap id (Q(read,"123")) in f “456”
是123
,但是let (Q(f,_)) = id (Q(read,"123")) in f "456"
是456
。事实上,
F
只是一个函子, - 它既不是函子,也不是反函子。不适用的合法仿函数,因为
pure
的类型签名无法实现:采用 Writer monad(a, w)
并删除w
应该是幺半群的约束。那么就不可能从a
构造出(a, w)
类型的值。不适用的仿函数,因为
<*>
的类型签名无法实现:data F a = Either (Int -> a) (String -> a)
.函子不合法适用,即使类型类方法可以实现:
类型构造函数
P
是一个函子,因为它使用 < code>a 仅在协变位置。<*>
类型签名的唯一可能实现是始终返回Nothing
的函数:但此实现不满足应用函子的恒等律。
Applicative
,但不是Monad
,因为bind
的类型签名无法实现。我不知道有这样的例子!
Applicative
,但不是Monad
,因为即使bind
的类型签名可以满足法则,也无法满足规则予以实施。这个例子引起了相当多的讨论,所以可以肯定地说,证明这个例子的正确性并不容易。但有几个人通过不同的方法独立验证了这一点。请参阅 `data PoE a = 空 |将 aa` 与 monad 配对? 进行额外讨论。
证明不存在合法的
Monad
实例有点麻烦。非单子行为的原因是,当函数f :: a -> 时,没有自然的方式来实现
可以返回bind
。对于a
的不同值,B bNothing
或Just
。考虑
Maybe (a, a, a)
(它也不是 monad)并尝试为此实现join
可能会更清楚。人们会发现没有直观合理的方法来实现join
。在
???
所示的情况下,很明显我们不能以任何合理且对称的方式从 6 个不同的值中生成Just (z1, z2, z3)
输入a
。我们当然可以选择这六个值的任意子集——例如,总是采用第一个非空的Maybe
——但这不能满足单子定律。返回Nothing
也不符合法律。bind
的关联性 - 但不符合恒等法则。通常的树状单子(或“具有函子形状分支的树”)被定义为
这是函子
f
上的自由单子。数据的形状是一棵树,其中每个分支点都是一个“功能齐全”的子树。标准二叉树将通过type fa = (a, a)
获得。如果我们通过将叶子也做成函子
f
的形状来修改这个数据结构,我们就得到了我所说的“半单子”——它具有满足自然性的bind
和结合律,但其pure
方法不符合恒等律之一。 “半单子是内函子范畴内的半群,有什么问题吗?”这是类型类Bind
。为了简单起见,我定义了
join
方法而不是bind
:分支嫁接是标准的,但叶子嫁接是非标准的,并产生一个
Branch
>。这对于结合律来说不是问题,但违反了恒等律之一。多项式类型什么时候有 monad 实例?
函子
Maybe (a, a)
和Maybe (a, a, a)
都不能被赋予合法的Monad
实例,尽管它们显然是适用性
。这些函子没有任何技巧 - 任何地方都没有
Void
或bottom
,没有棘手的惰性/严格性,没有无限的结构,也没有类型类约束。Applicative
实例是完全标准的。可以为这些函子实现函数return
和bind
,但不满足monad的法则。换句话说,这些函子不是单子,因为缺少特定的结构(但很难理解到底缺少什么)。举个例子,函子中的一个小变化可以使其成为一个 monad:data Maybe a = Nothing |只是 a
就是一个单子。另一个类似的函子data P12 a = Either a (a, a)
也是一个 monad。多项式单子的构造
一般来说,这里有一些从多项式类型中产生合法
Monad
的构造。在所有这些结构中,M
是一个 monad:type M a = Either c (w, a)
其中w
是任何幺半群type M a = m (Either c (w, a))
其中m
是任何 monad,w
是任何 monoidtype M a = ( m1 a, m2 a)
其中m1
和m2
是任何 monadtype M a = Either a (ma)
其中m
是任何 monad第一个构造是
WriterT w (Either c)
,第二个构造是WriterT w (EitherT cm)
。第三个构造是 monad 的组件式乘积:pure @M
被定义为pure @m1
和pure @m2
的组件式乘积code> ,并且join @M
是通过省略叉积数据来定义的(例如m1 (m1 a, m2 a)
映射到m1 (m1 a)
通过省略元组的第二部分):第四个构造被定义为
我已经检查所有四个构造都产生合法的单子。
我推测多项式单子没有其他结构。例如,函子
Maybe (Either (a, a) (a, a, a, a))
不是通过任何这些构造获得的,因此不是一元的。然而,Either (a, a) (a, a, a)
是一元的,因为它与三个单元的乘积同构a
,a
和也许是
。另外,Either (a,a) (a,a,a,a)
是一元的,因为它与a
和Either a (a ,a,a)
。上面显示的四种结构将允许我们获得任意数量的
a
的任意数量的乘积的任意总和,例如Either (Either (a, a) (a, a, a, a)) (a, a, a, a, a))
等等。所有此类类型构造函数都将具有(至少一个)Monad
实例。当然,此类单子可能存在哪些用例还有待观察。另一个问题是通过构造 1-4 派生的 Monad 实例通常不是唯一的。例如,类型构造函数
type F a = Either a (a, a)
可以通过两种方式给出一个Monad
实例:通过使用 monad构造 4 (a, a)
,并通过使用类型同构Either a (a, a) = (a, Maybe a)
的构造 3。同样,找到这些实现的用例并不是立即显而易见的。仍然存在一个问题 - 给定任意多项式数据类型,如何识别它是否具有
Monad
实例。我不知道如何证明多项式单子不存在其他结构。我认为到目前为止还没有任何理论可以回答这个问题。I'd like to propose a more systematic approach to answering this question, and also to show examples that do not use any special tricks like the "bottom" values or infinite data types or anything like that.
When do type constructors fail to have type class instances?
In general, there are two reasons why a type constructor could fail to have an instance of a certain type class:
Examples of the first kind are easier than those of the second kind because for the first kind, we just need to check whether one can implement a function with a given type signature, while for the second kind, we are required to prove that no implementation could possibly satisfy the laws.
Specific examples
A type constructor that cannot have a functor instance because the type cannot be implemented:
This is a contrafunctor, not a functor, with respect to the type parameter
a
, becausea
in a contravariant position. It is impossible to implement a function with type signature(a -> b) -> F z a -> F z b
.A type constructor that is not a lawful functor even though the type signature of
fmap
can be implemented:The curious aspect of this example is that we can implement
fmap
of the correct type even thoughF
cannot possibly be a functor because it usesa
in a contravariant position. So this implementation offmap
shown above is misleading - even though it has the correct type signature (I believe this is the only possible implementation of that type signature), the functor laws are not satisfied. For example,fmap id
≠id
, becauselet (Q(f,_)) = fmap id (Q(read,"123")) in f "456"
is123
, butlet (Q(f,_)) = id (Q(read,"123")) in f "456"
is456
.In fact,
F
is only a profunctor, - it is neither a functor nor a contrafunctor.A lawful functor that is not applicative because the type signature of
pure
cannot be implemented: take the Writer monad(a, w)
and remove the constraint thatw
should be a monoid. It is then impossible to construct a value of type(a, w)
out ofa
.A functor that is not applicative because the type signature of
<*>
cannot be implemented:data F a = Either (Int -> a) (String -> a)
.A functor that is not lawful applicative even though the type class methods can be implemented:
The type constructor
P
is a functor because it usesa
only in covariant positions.The only possible implementation of the type signature of
<*>
is a function that always returnsNothing
:But this implementation does not satisfy the identity law for applicative functors.
Applicative
but not aMonad
because the type signature ofbind
cannot be implemented.I do not know any such examples!
Applicative
but not aMonad
because laws cannot be satisfied even though the type signature ofbind
can be implemented.This example has generated quite a bit of discussion, so it is safe to say that proving this example correct is not easy. But several people have verified this independently by different methods. See Is `data PoE a = Empty | Pair a a` a monad? for additional discussion.
It is somewhat cumbersome to prove that there is no lawful
Monad
instance. The reason for the non-monadic behavior is that there is no natural way of implementingbind
when a functionf :: a -> B b
could returnNothing
orJust
for different values ofa
.It is perhaps clearer to consider
Maybe (a, a, a)
, which is also not a monad, and to try implementingjoin
for that. One will find that there is no intuitively reasonable way of implementingjoin
.In the cases indicated by
???
, it seems clear that we cannot produceJust (z1, z2, z3)
in any reasonable and symmetric manner out of six different values of typea
. We could certainly choose some arbitrary subset of these six values, -- for instance, always take the first nonemptyMaybe
- but this would not satisfy the laws of the monad. ReturningNothing
will also not satisfy the laws.bind
- but fails the identity laws.The usual tree-like monad (or "a tree with functor-shaped branches") is defined as
This is a free monad over the functor
f
. The shape of the data is a tree where each branch point is a "functor-ful" of subtrees. The standard binary tree would be obtained withtype f a = (a, a)
.If we modify this data structure by making also the leaves in the shape of the functor
f
, we obtain what I call a "semimonad" - it hasbind
that satisfies the naturality and the associativity laws, but itspure
method fails one of the identity laws. "Semimonads are semigroups in the category of endofunctors, what's the problem?" This is the type classBind
.For simplicity, I define the
join
method instead ofbind
:The branch grafting is standard, but the leaf grafting is non-standard and produces a
Branch
. This is not a problem for the associativity law but breaks one of the identity laws.When do polynomial types have monad instances?
Neither of the functors
Maybe (a, a)
andMaybe (a, a, a)
can be given a lawfulMonad
instance, although they are obviouslyApplicative
.These functors have no tricks - no
Void
orbottom
anywhere, no tricky laziness/strictness, no infinite structures, and no type class constraints. TheApplicative
instance is completely standard. The functionsreturn
andbind
can be implemented for these functors but will not satisfy the laws of the monad. In other words, these functors are not monads because a specific structure is missing (but it is not easy to understand what exactly is missing). As an example, a small change in the functor can make it into a monad:data Maybe a = Nothing | Just a
is a monad. Another similar functordata P12 a = Either a (a, a)
is also a monad.Constructions for polynomial monads
In general, here are some constructions that produce lawful
Monad
s out of polynomial types. In all these constructions,M
is a monad:type M a = Either c (w, a)
wherew
is any monoidtype M a = m (Either c (w, a))
wherem
is any monad andw
is any monoidtype M a = (m1 a, m2 a)
wherem1
andm2
are any monadstype M a = Either a (m a)
wherem
is any monadThe first construction is
WriterT w (Either c)
, the second construction isWriterT w (EitherT c m)
. The third construction is a component-wise product of monads:pure @M
is defined as the component-wise product ofpure @m1
andpure @m2
, andjoin @M
is defined by omitting cross-product data (e.g.m1 (m1 a, m2 a)
is mapped tom1 (m1 a)
by omitting the second part of the tuple):The fourth construction is defined as
I have checked that all four constructions produce lawful monads.
I conjecture that there are no other constructions for polynomial monads. For example, the functor
Maybe (Either (a, a) (a, a, a, a))
is not obtained through any of these constructions and so is not monadic. However,Either (a, a) (a, a, a)
is monadic because it is isomorphic to the product of three monadsa
,a
, andMaybe a
. Also,Either (a,a) (a,a,a,a)
is monadic because it is isomorphic to the product ofa
andEither a (a, a, a)
.The four constructions shown above will allow us to obtain any sum of any number of products of any number of
a
's, for exampleEither (Either (a, a) (a, a, a, a)) (a, a, a, a, a))
and so on. All such type constructors will have (at least one)Monad
instance.It remains to be seen, of course, what use cases might exist for such monads. Another issue is that the
Monad
instances derived via constructions 1-4 are in general not unique. For example, the type constructortype F a = Either a (a, a)
can be given aMonad
instance in two ways: by construction 4 using the monad(a, a)
, and by construction 3 using the type isomorphismEither a (a, a) = (a, Maybe a)
. Again, finding use cases for these implementations is not immediately obvious.A question remains - given an arbitrary polynomial data type, how to recognize whether it has a
Monad
instance. I do not know how to prove that there are no other constructions for polynomial monads. I don't think any theory exists so far to answer this question.