首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >一些范畴论上的概念

一些范畴论上的概念

作者头像
Orlion
发布于 2024-09-02 08:22:59
发布于 2024-09-02 08:22:59
27500
代码可运行
举报
文章被收录于专栏:我的独立博客我的独立博客
运行总次数:0
代码可运行

为了能真正理解Haskell中的Functor、Applicative、Monad、Monoid,以及它们到底有什么用,个人觉得还是有必要 了解 一些范畴论里面的概念的

函数 Function

函数表示特定类型之间的 态射

自函数 EndoFunction

自函数就是把类型映射到自身类型

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
identity :: Number -> Number

identity函数就是一个自函数的例子,它接收什么就返回什么

函子 Functor

函子与函数不同,函数描述的是类型之间的映射,而函子描述的是 范畴(category) 之间的映射

范畴

范畴是一组类型及其关系 态射 的集合。包括特定类型及其态射,比如: Int、 String、 Int -> String ;高阶类型及其态射,比如 List[Int]、 List[String]、 List[Int] -> List[String]

函子如何映射两个范畴

图中,范畴C1和范畴c2之间有映射关系,C1中Int映射到C2List[Int],C1中String映射到C2List[String],C1中的关系态射Int -> String 也映射到 C2中的关系List[Int] -> List[String]态射上。

也就是说,一个范畴内部的所有元素可以映射为另一个范畴的元素,且元素间的关系也可以映射为另一范畴中的元素间的关系,则设为这两个范畴之间存在映射。所谓函子就是表示两个范畴之间的映射。

Haskell中,Functor是可以被map over的东西,List就是一个典型的instance。构造List[Int] 就是把Int提升到List[Int],记作:Int -> List[Int] . 这表达了一个范畴的元素可以被映射为另一个范畴的元素

我们看下Haskell中map函数的定义:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
map :: (a -> b) -> [a] -> [b]

把我们上面的Int String的例子代入,配合柯里化的概念可以得出:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
map :: (Int -> String) -> (List[Int] -> List[String])

map的定义清晰的告诉我们: Int -> String 这个关系可以被映射为 List[Int] -> List[String] 这种关系。这就表达了元素间的关系可以映射为另外一个范畴元素间的关系

所以List就是一个Functor

自函子

自函数是把类型映射到自身类型,那么自函子就是把范畴映射到自身范畴。

上图就是一个将范畴映射到自身的自函子。从函子的定义出发,我们考察这个自函子,始终有List[Int] -> List[String]List[Int] -> List[String] -> List[Int] -> List[String] 这两种映射。我们表述为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
类型List[Int] 映射到自己
态射f :: List[Int] -> List[String] 映射到自己

我们记作:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
F(List[Int]) = List[Int]
F(f) = f
其中F是Functor

幺半群

先解释下群的概念:G为非空集合,如果在G上定义的二元运算*,满足:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
(1) 封闭性:(Closure):对于任意a,b∈G,有a*b∈G
(2) 结合律(Associativity):对于任意a,b,c∈G,有(a*b)*c=a*(b*c)
(3) 幺元 (Identity):存在幺元e,使得对于任意a∈G,e*a=a*e=a
(4) 逆元:对于任意a∈G,存在逆元a^-1,使得a^-1*a=a*a^-1=e

则称(G, *) 为群,简称G为群。

如果仅满足封闭性和结合律,则该G是一个 半群(Semigroup) ; 如果满足封闭性和结合律并且存在幺元,则该G是一个 幺半群(Monoid)

接下来看下在自函子的范畴上,怎样结合幺半群的定义得出Monad

假设我们有个cube函数,它计算一个数的三次方:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
cube :: Number -> Number

现在我们想在其返回值上添加一些调试信息,返回一个元组,第二个元素代表调试信息,函数签名为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
f :: Number -> (Number, String)

可以看到参数与返回值不一致。我们再看下幺半群规定的结合律。对于函数而言,结合律就是将函数以各种结合方式嵌套起来调用。我们将Haskell中的 . 函数看做这里的二元运算。

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

f . f

从函数签名可以看出右边f返回的是元组(Number, String),而左侧的f接收的是Number。所以无法组合,他们彼此不兼容。

有什么办法能消除这种不兼容?结合前面所述,cube是一个自函数,元组(Number,String)在Hask范畴是一个自函子 (这个说法看起来并不准确,(?, String)才应该是一个自函子 ) , 理由如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
F Number = (Number, String)
F Number -> Number = (Number,String) -> (Number,String)

如果输入和输出都是元组,结果会怎样呢?

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
fn :: (Number,String) -> (Number,String)
fn . fn

这样是可行的,在验证满足结合律之前,我们引入一个liftM函数来辅助将f提升成fn

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
liftM :: (Double -> (Double, String)) -> (Double,String) -> (Double, String)
liftM f (x,y) = case r of (n,s) -> (n, y ++ s)
    where r = f x 

没有验证,就当伪代码看吧

我们来实现元组自函子范畴上的结合律:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
cube :: Number -> (Number, String)
cube x = (x * x * x, "cube was called.")

sine :: Number -> (Number, String)
sine x = (Math.sin x, "sine was called.")

f = ((liftM sine) . (liftM cube)) . (liftM cube)
f (3, "")
输出:(0.956, 'cube was called.cube was called.sine was called.')

f1 = (liftM sine) . ((liftM cube) . (liftM cube))
输出:(0.956, 'cube was called.cube was called.sine was called.')

这里f和f1代表的结合顺序产生了相同的结果,说明元组自函子范畴满足结合律。

那如何找到这样一个e,使得 a * e = e * a = a ,此处的 * 就是 .

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
unit :: Number -> (Number, String)
unit x = (x, "")

f = (liftM sine) . (liftM cube)

f . (liftM unit) = (liftM unit) . f = f

这里的 liftM unit 就是 e 了。

unit 个人理解应该就是类型构造器

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-01-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
JavaScript 第四天
函数可把相同逻辑代码包裹起来, 通过函数调用执行包裹的代码, 有利精简代码方便复用
小城故事
2023/03/10
2570
探索JavaScript函数---基础篇
函数可以把具有相同或相似逻辑的代码“包裹”起来,通过函数调用执行这些被“包裹”的代码逻辑,这么做的优势是有利于精简代码方便复用。
P_M_P
2024/06/02
1760
探索JavaScript函数---基础篇
JS基础(三)
当调用某个函数,这个函数会返回出一个结果。 不是所有的函数都需要返回值,比如 alert(‘弹框’)
且陶陶
2023/04/12
3900
JS基础(三)
前端架构师进阶之路07_JavaScript函数
相当于将一条或多条语句组成的代码块包裹起来,在使用时只需关心参数和返回值,就能完成特定的功能,而不用了解具体的实现。
张哥编程
2024/12/13
2120
前端架构师进阶之路07_JavaScript函数
javascript函数基础
形参:声明函数时写在函数名右边小括号里的叫形参(形式上的参数) 实参:调用函数时写在函数名右边小括号里的叫实参(实际上的参数)
timerring
2023/10/13
1910
javascript函数基础
JS函数(号称最全最详解包括es6)
第一绝: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head
贵哥的编程之路
2020/10/30
4680
JS函数(号称最全最详解包括es6)
23·灵魂前端工程师养成-JavaScript函数
-多年互联网运维工作经验,曾负责过大规模集群架构自动化运维管理工作。 -擅长Web集群架构与自动化运维,曾负责国内某大型金融公司运维工作。 -devops项目经理兼DBA。 -开发过一套自动化运维平台(功能如下): 1)整合了各个公有云API,自主创建云主机。 2)ELK自动化收集日志功能。 3)Saltstack自动化运维统一配置管理工具。 4)Git、Jenkins自动化代码上线及自动化测试平台。 5)堡垒机,连接Linux、Windows平台及日志审计。 6)SQL执行及审批流程。 7)慢查询日志分析web界面。
DriverZeng
2022/10/31
3930
23·灵魂前端工程师养成-JavaScript函数
JavaScript第五节
全局作用域 :在script标签内,函数外的区域就是全局作用域,在全局作用内声明的变量叫做全局变量 。全局变量可以在任意地方访问。
用户3461357
2019/08/02
6960
JavaScript基础07--函数
封装了一段可被重复调用执行的代码块,通过函数可以实现大量代码的重复使用。函数是一种数据类型。
软件架构师Michael
2022/08/02
2120
JavaScript——函数
在JS里面,可能会定义非常多的相同代码或者功能相似的代码,这些代码可能需要大量重复使用。
岳泽以
2022/10/26
9830
Javascript提升阶段学习
JavaScript 1:javascript简介   JavaScript是一种脚本语言,能实现网页内容的交互显示,当用户在客户端显示该网页时,浏览器就会执行JavaScript程序,用户通过交互
别先生
2017/12/29
1.4K0
「JavaScript」数组与函数
请注意,本文编写于 2099 天前,最后修改于 174 天前,其中某些信息可能已经过时。
曼亚灿
2023/05/17
6140
「JavaScript」数组与函数
翻译连载 |《你不知道的JS》姊妹篇 |《JavaScript 轻量级函数式编程》- 第 2 章:函数基础
原文地址:Functional-Light-JS 原文作者:Kyle Simpson-《You-Dont-Know-JS》作者 第 2 章:函数基础 函数式编程不是仅仅用 function 这个关键词
iKcamp
2018/01/04
1.7K0
翻译连载 |《你不知道的JS》姊妹篇 |《JavaScript 轻量级函数式编程》- 第 2 章:函数基础
前端基础-JavaScript函数进阶
这种写法将一个匿名函数赋值给变量。这时,这个匿名函数又称函数表达式(Function Expression)
cwl_java
2020/03/26
5800
后端眼中的JavaScript长啥样?这篇文章告诉你。
数组是指一组数据的集合,其中的每个数据被称作元素,在数组中可以存放任意类型的元素。数组是一种将一组数据存储在单个变量名下的优雅方式。数组可以把一组相关的数据一起存放,并提供方便的访问(获取)方式。
上分如喝水
2022/02/26
9100
后端眼中的JavaScript长啥样?这篇文章告诉你。
前端js基础教程
Netscape在最初将其脚本语言命名为LiveScript,后来Netscape在与Sun合作之后将其改名为JavaScript。JavaScript最初受Java启发而开始设计的,目的之一就是“看上去像Java”,因此语法上有类似之处,一些名称和命名规范也借自Java。JavaScript与Java名称上的近似,是当时Netscape为了营销考虑与Sun微系统达成协议的结果。Java和JavaScript的关系就像张雨生和张雨的关系,只是名字很像。
张哥编程
2024/12/13
2920
前端js基础教程
JavaScript基础
「计算机语言」分为机器语言,汇编语言,高级语言。计算机内部最终执行的都是机器语言,由0和1这样的二进制数构成。
小城故事
2023/03/10
1.1K0
JavaScript基础
JavaSprict函数
盛透侧视攻城狮
2024/10/21
810
JavaScript进阶-01
作用域(scope)规定了变量能够被访问的“范围”,离开了这个“范围”变量便不能被访问,作用域分为全局作用域和局部作用域。
yuanshuai
2022/08/23
7830
JavaScript 进阶 - 第1天
作用域(scope)规定了变量能够被访问的“范围”,离开了这个“范围”变量便不能被访问,作用域分为全局作用域和局部作用域。
用户10169043
2022/11/18
8860
相关推荐
JavaScript 第四天
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档