首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >可折叠、Monoid和Monad

可折叠、Monoid和Monad
EN

Stack Overflow用户
提问于 2016-10-10 05:27:24
回答 3查看 2.6K关注 0票数 27

考虑foldMap的以下签名

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
foldMap :: (Foldable t, Monoid m) => (a -> m) -> t a -> m

这非常类似于"bind",只是交换了参数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
(>>=) :: Monad m => m a -> (a -> m b) -> m b

因此,在我看来,FoldableMonoidMonad之间一定存在某种关系,但我在超类中找不到它。大概我可以将其中的一两个转换成另一个,但我不确定如何转换。

这种关系可能是详细的吗?

EN

回答 3

Stack Overflow用户

发布于 2016-10-10 07:08:15

MonoidMonad

哇,这实际上是我们可以使用这句名言的极少数几次之一:

单子只是内函子范畴中的么半群,...

让我们从一个么半群开始。集合的类别Set中的么半群是具有空元素mempty和组合元素的结合函数mappend的元素m的集合,使得

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
mempty `mappend` x == x -- for any x
x `mappend` mempty == x -- for any x
-- and
a `mappend` (b `mappend` c) == (a `mappend` b) `mappend` c -- for all a, b, c

请注意,么半群并不局限于集合,在类别(单体)的范畴Cat中也存在么半群,依此类推。基本上,任何时候你都有一个关联的二元操作和它的身份。

现在monad具有以下属性,它是“内函数范畴中的么半群”:

它是一个内部函数,这意味着它在Haskell类型的类别Hask中有* -> *类型。

现在,为了更进一步,你必须了解一点范畴理论,我将在这里尝试解释:给定两个函子FG,存在从FG的自然转换当且仅当存在一个函数α,使得每个F a都可以映射到一个G aα可以是多对一的,但它必须映射F a的每个元素。粗略地说,自然变换是函数器之间的函数。

现在在范畴论中,两个范畴之间可以有许多函子。在一个简化的视图中,可以说我们甚至不关心哪些函数从哪里映射到哪里,我们只关心它们之间的自然转换。

回到monad,我们现在可以看到,“内函数式类别中的么半群”必须有两个自然转换。让我们将monad内部函数命名为M

从identity (endo)functor到monad的自然转换:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
η :: 1 -> M -- this is return

以及从两个单子的组合自然转换并产生第三个单子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
μ :: M × M -> M

由于×是函数器的组合,我们(粗略地说)还可以写:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
μ :: m a × m a -> m a
μ :: (m × m) a -> m a
μ :: m (m a) -> m a -- join in Haskell

满足这些定律:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
μ . M μ == μ . μ M
μ . M η == μ . η M

因此,单子是内函子范畴中么半群的一个特例。你不能在普通的Haskell中为monad编写monoid实例,因为Haskell的组合概念太弱了(我认为;这是因为函数被限制在Hask中,而且它比Cat弱)。有关详细信息,请参阅this

Foldable呢?

现在对于Foldable:已经有了fold的定义,使用一个自定义的二进制函数来组合元素。现在,您当然可以提供任何函数来组合元素,也可以使用现有的组合元素的概念,即monoid。同样,请注意,这个么半群仅限于集合么半群,而不是么半群的分类定义。

由于么半群的mappend是结合的,所以foldlfoldr产生相同的结果,这就是为什么么半群的折叠可以简化为fold :: Monoid m, Foldable t => t m -> m。这是monoid和foldable之间的一个明显的联系。

@danidiaz已经使用Const函数器Const a b = Const a指出了ApplicativeMonoidFoldable之间的联系,其应用实例要求Const的第一个参数是么半群(没有pure而没有mempty (忽略undefined))。

在我看来,比较monad和foldable有点牵强,因为monad比foldable更强大,因为foldable只能根据映射函数累积列表的值,但monad绑定可以在结构上改变上下文(a -> m b)。

票数 26
EN

Stack Overflow用户

发布于 2016-10-10 09:42:40

总结(>>=)traverse看起来很相似,因为它们都是函数器的箭头映射,而foldMap (几乎)是一个专门的traverse

在我们开始之前,有一点术语需要解释。考虑fmap

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
fmap :: Functor f => (a -> b) -> (f a -> f b)

Haskell Functor是从Hask类别( Haskell用作箭头的类别)到其自身的函数器。在范畴理论术语中,我们说(专用的) fmap是这个函数器的箭头映射,因为它是函数器的一部分,将箭头带到箭头。(出于完整性的考虑: functor由一个箭头映射和一个对象映射组成。在本例中,对象是Haskell类型,因此对象映射将类型映射为类型--更具体地说,Functor的对象映射是它的类型构造函数。)

我们还需要记住类别和函数法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
-- Category laws for Hask:
f . id = id
id . f = f
h . (g . f) = (h . g) . f

-- Functor laws for a Haskell Functor:
fmap id = id
fmap (g . f) = fmap g . fmap f

在接下来的工作中,我们将使用Hask以外的类别,以及不是Functor的functor。在这种情况下,我们将用适当的标识和组合替换id(.),用适当的箭头映射替换fmap,在一种情况下,用适当的箭头相等替换=

(=<<)

首先从答案中更熟悉的部分开始,对于给定的单体ma -> m b函数(也称为克莱斯利箭头)形成一个类别(m的克莱斯利类别),其中return是身份,(<=<)是组合。在本例中,这三个类别法则就是the monad laws

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
f <=< return = return
return <=< f = f
h <=<  (g <=<  f) = (h <=<  g) <=<  f

现在,您被问及翻转绑定:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
(=<<) :: Monad m => (a -> m b) -> (m a -> m b)

事实证明,(=<<)是从m的Kleisli类别到Hask的函数器的箭头映射。适用于(=<<)的函子定律相当于两个单子定律:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
return =<< x = x -- right unit
(g <=< f) =<< x = g =<< (f =<< x) -- associativity 

遍历

接下来,我们需要绕道通过Traversable (在答案的末尾提供了本节结果证明的草图)。首先,我们注意到一次获取所有应用函数器fa -> f b函数(而不是每次一个)形成一个类别,其中Identity为标识,Compose . fmap g . f为组合。为此,我们还必须采用更宽松的箭头相等,它忽略了IdentityCompose样板(这是唯一必要的,因为我是用伪Haskell编写的,而不是正确的数学符号)。更准确地说,我们将考虑可以使用IdentityCompose同构的任何组合作为相等箭头进行相互转换的任何两个函数(或者,换句话说,我们将不区分aIdentity a,也不区分f (g a)Compose f g a)。

让我们将该类别称为“可遍历类别”(因为我现在想不出更好的名称)。在具体的Haskell术语中,这个类别中的箭头是一个函数,它在任何先前存在的层的“下面”添加一个额外的Applicative上下文层。现在,考虑一下traverse

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
traverse :: (Traversable t, Applicative f) => (a -> f b) -> (t a -> f (t b))

给定可遍历容器的选择,traverse是函数从“可遍历类别”到自身的箭头映射。它的函数律相当于可穿越律。

简而言之,(=<<)traverse都是fmap的类似物,涉及Hask之外的其他类别的函数器,因此它们的类型有点相似也就不足为奇了。

foldMap

我们仍然需要解释所有这些与foldMap有什么关系。答案是可以从traverse恢复foldMap (参见danidiaz's answer --它使用traverse_,但由于应用函数器为Const m,因此结果本质上是相同的):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
-- cf. Data.Traversable
foldMapDefault :: (Traversable t, Monoid m) => (a -> m) -> (t a -> m)
foldMapDefault f = getConst . traverse (Const . f)

由于const/getConst同构,这显然等同于:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
foldMapDefault' :: (Traversable t, Monoid m)
                => (a -> Const m b) -> (t a -> Const m (t b))
foldMapDefault' f = traverse f

这只是专门针对Monoid m => Const m应用函数器的traverse。即使Traversable不是FoldablefoldMapDefault也不是foldMap,这也为为什么foldMap的类型类似于traverse(=<<)的类型提供了一个很好的理由。

作为最后的观察,请注意,对于某些Monoid m,带有应用函数器Const m的“可遍历类别”的箭头不会形成子类别,因为除非Identity是应用函数器的可能选择之一,否则没有身份。这可能意味着从这个答案的角度来看,关于foldMap没有其他有趣的东西可说。给出子类别的应用函数器的唯一选择是Identity,这并不奇怪,因为使用Identity遍历相当于容器上的fmap

附录

这是从几个月前我的笔记中提取的traverse结果的粗略草图,经过最少的编辑。~的意思是“等同于(一些相关的)同构”。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
-- Identity and composition for the "traversable category".
idT = Identity
g .*. f = Compose . fmap g . f

-- Category laws: right identity
f .*. idT ~ f
f .*. idT
Compose . fmap f . idT
Compose . fmap f . Identity
Compose . Identity . f
f -- using getIdentity . getCompose 

-- Category laws: left identity
idT .*. f ~ f
idT .*. f
Compose . fmap Identity . f
f -- using fmap getIdentity . getCompose

-- Category laws: associativity
h .*. (g .*. f) ~ (h .*. g) .*. f
h .*. (g .*. f) -- LHS
h .*. (Compose . fmap g . f)
Compose . fmap h . (Compose . fmap g . f)
Compose . Compose . fmap (fmap h) . fmap g . f
(h .*. g) .*. f -- RHS
(Compose . fmap h . g) .*. f
Compose . fmap (Compose . fmap h . g) . f
Compose . fmap (Compose . fmap h) . fmap g . f
Compose . fmap Compose . fmap (fmap h) . fmap g . f
-- using Compose . Compose . fmap getCompose . getCompose
Compose . Compose . fmap (fmap h) . fmap g . f -- RHS ~ LHS
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
-- Functor laws for traverse: identity
traverse idT ~ idT
traverse Identity ~ Identity -- i.e. the identity law of Traversable

-- Functor laws for traverse: composition
traverse (g .*. f) ~ traverse g .*. traverse f
traverse (Compose . fmap g . f) ~ Compose . fmap (traverse g) . traverse f 
-- i.e. the composition law of Traversable
票数 10
EN

Stack Overflow用户

发布于 2016-10-10 06:11:21

当容器为Foldable时,foldMapApplicative (Monad的超类)之间存在关系。

Foldable有一个名为traverse_的函数,带签名:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
traverse_ :: Applicative f => (a -> f b) -> t a -> f ()

一种可能的ApplicativeConstant。要成为应用程序,它需要“Monoid”参数是一个累加器

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
newtype Constant a b = Constant { getConstant :: a } -- no b value at the term level!

Monoid a => Applicative (Constant a)

例如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
gchi> Constant (Sum 1) <*> Constant (Sum 2) :: Constant (Sum Int) whatever
Constant (Sum {getSum = 3})

我们可以这样根据traverse_Constant来定义foldMap

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
foldMap' :: (Monoid m, Foldable t) => (a -> m) -> t a -> m
foldMap' f = getConstant . traverse_ (Constant . f)

我们使用traverse_遍历容器,用Constant累加值,然后使用getConstant去掉newtype。

票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/39951758

复制
相关文章
display属性值有哪些_验证控件display属性
<html> <head> <meta charset=”utf-8″> <title>W3Cschool教程(w3cschool.cn)</title> <style> #main { width: 70px; height: 300px; border: 1px solid #c3c3c3; display: -webkit-flex; display: flex; /* 设置flex布局*/ -webkit-flex-wrap: wrap; flex-wrap: wrap;/*换行*/ -webkit-align-content: center; align-content: center; /*垂直对齐*/ }
全栈程序员站长
2022/11/01
2.2K0
display属性值有哪些_验证控件display属性
有什么方法可以快速筛选出 pitch 中的值 在0.2 > x > -0.2 的值?
前几天在Python钻石交流群有个叫【进击的python】的粉丝问了一个Python基础的问题,这里拿出来给大家分享下,一起学习下。
前端皮皮
2022/08/17
1.2K0
有什么方法可以快速筛选出 pitch 中的值 在0.2 > x > -0.2 的值?
Spring Boot 定义接口的方法是否可以声明为 private?
估计很少有人会把接口方法定义成 private 的吧?那我们不禁要问,如果非要定义成 private 的方法,那能运行起来吗?
江南一点雨
2021/04/02
6040
Java比较两个对象中属性值是否相同【使用反射实现】
在工作中,有些场景下,我们需要对比两个完全一样对象的属性值是否相等。比如接口替换的时候,需要比较新老接口在相同情况下返回的数据是否相同。这个时候,我们怎么处理呢?这里凯哥就使用Java的反射类实现。
凯哥Java
2022/12/16
3.6K0
用画小狗的方法来解释Java中的值传递
答案是 A旺财,changeName方法并没有把myDog的名称改了。如果你答错了,没关系,我要开始画小狗了,画完你就明白了;如果你答对了,但不太明白其中的原因,那我画的小狗也肯定能帮到你。
凯哥Java
2019/07/01
8900
【javascript】hasOwnProperty()方法检查对象是否有该属性
hasOwnProperty() 只会检查对象的自有属性,对象原形上的属性其不会检测;但是对于原型对象本身来说,这些原型上的属性又是原型对象的自有属性,所以原形对象也可以使用hasOwnProperty()检测自己的自有属性
唯一Chat
2022/09/27
1.1K0
使用JSON Schema来验证接口数据
该文介绍了如何使用JSON Schema来验证接口数据。JSON Schema是一种用于定义和验证JSON数据结构的工具。它可以定义一个JSON对象,并指定该对象中哪些字段是必须的,哪些字段是可选的,以及字段的类型和约束。使用JSON Schema可以方便地验证JSON数据是否符合预期的格式,并确保数据中的字段类型和约束符合要求。该文还介绍了如何使用Dojo Toolkit中的JSON Schema验证模块来验证JSON数据,并提供了相关的示例代码。
IMWeb前端团队
2017/12/29
2.3K0
使用JSON Schema来验证接口数据
最近在做一些关于JSON Schema的基建,JSON Schema可以描述一个JSON结构,那么反过来他也可以来验证一个JSON是否符合期望的格式,于是我想到之前lqlong 之前的一篇 《数据字段防卫探索》 发现JSON Schema可以做这个事情。
IMWeb前端团队
2019/12/04
2.1K0
Java 中如何判断数组中是否包含某个值?
public static boolean useLoop(String[] arr, String targetValue) { for(String s: arr){ if(s.equals(targetValue)) return true; } return false; }
水货程序员
2018/11/13
5.9K0
java9-可以在interface中定义私有方法了
在传统的Java编程中,被广为人知的一个知识点是:java Interface接口中不能定义private私有方法。只允许我们定义public访问权限的方法、抽象方法或静态方法。但是从Java 9 开始,Interface 接口中允许定义私有方法和私有静态方法。下面我们就来为大家介绍其语法规则,和为什么要有这样的设计。
字母哥博客
2020/10/14
1.7K0
如何检查 Java 数组中是否包含某个值 ?
在逛 programcreek 的时候,我发现了一些专注细节但价值连城的主题。比如说:如何检查Java数组中是否包含某个值 ?像这类灵魂拷问的主题,非常值得深入地研究一下。
用户7886150
2021/04/28
9.1K0
Java之判断数组中是否包含某个值
方式一:使用Arrays.asList(str).contains() public static boolean useList(String[] arr, String targetValue) { return Arrays.asList(arr).contains(targetValue); } 示例如下: String[] str={"学历教育","专业教育","通识教育","其它在职训"} ; if (!Arrays.asList(st
IT工作者
2022/02/12
4.2K0
Java|方法的定义(形参、实参、返回值)
方法很类似于面向过程中的函数。面向过程中,函数是最基本单位,整个程序由一个个函数调用组成。
算法与编程之美
2020/08/06
1.8K0
JNI:取得Java属性和设置Java属性值
可以使用GetObjectField来获取数组类型的属性。不过JNI提供了一系列方法获取数组的属性,后续介绍。
BUG弄潮儿
2022/03/08
2K0
一种自动的将自定义类序列化为JSON的方法
最近因为项目需求,需要将一些自定义的类序列化为JSON,网上有很多好用的第三方序列化工具,但都只能自动序列化一些基本类型,如NSNumber,NSString与NSDictionary这种,没有一种第三方工具提供直接将自定义类序列化的方法(至少据我所知:),而对于这种序列化自定义的类的需求,网上能查到的方法只有将自定义的类手动的转存为一个NSDictionary,然后再使用第三方工具来序列化。例如对于一个类Foo,有如下定义: @interface Foo : NSObject {   NSStri
阿新
2018/04/12
1.1K0
jquery判断数组中是否包含某个元素的值_java判断元素是否在数组中
如果arry数组里面存在”C#” 这个字符串则返回该字符串的数组下标,否则返回(不包含在数组中) -1
全栈程序员站长
2022/11/02
5.8K0
java 根据list对象中的一个属性值是否一致去重的简单做法
创建一个临时的数组 筛选要去重的数组,把对象是不重复的放到临时创建的数组中 判断这个去重的数组在不重复的数组中是否存在 再获得这个数组 一个foreach/for一定可以用stream来处理,去重也可以用lambda表达式来做 List<ApprovalChainInfoData> temp = new ArrayList<>(); approvalChainInfoData = approvalChainInfoData1.stream().filter(i -> {
gfu
2019/11/14
4.2K0
Java中interface属性和实例方法
A.接口Nameable无法编译。B.类Employee无法编译。C.类HR无法编译。D.输出John Doe。
FunTester
2020/02/17
2K0
【源头活水】一种高效评估预训练模型是否适合当前任务的方法
“问渠那得清如许,为有源头活水来”,通过前沿领域知识的学习,从其他研究领域得到启发,对研究问题的本质有更清晰的认识和理解,是自我提高的不竭源泉。为此,我们特别精选论文阅读笔记,开辟“源头活水”专栏,帮助你广泛而深入的阅读科研文献,敬请关注。
马上科普尚尚
2021/04/07
8220
【源头活水】一种高效评估预训练模型是否适合当前任务的方法
Java中抽象类和接口中可以有构造方法吗?
Java中抽象类和接口中可以有构造方法吗? -----------------------------------------------------------------------------
黑泽君
2018/10/11
2K0

相似问题

在AWS EC2免费层帐户上安装码头

13

AWS免费层上的Windows和Linux同时EC2实例

12

如何在AWS免费层上安装Laravel?

20

EC2的AWS免费层限制

16

无法在AWS免费层中启动第二个实例实例

13
添加站长 进交流群

领取专属 10元无门槛券

AI混元助手 在线答疑

扫码加入开发者社群
关注 腾讯云开发者公众号

洞察 腾讯核心技术

剖析业界实践案例

扫码关注腾讯云开发者公众号
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文