chengaofeng
发布于 2024-10-14 / 11 阅读
0
0

Getting started with fp-ts: Category

原文:https://dev.to/gcanti/getting-started-with-fp-ts-category-4c9a

这篇文章的标题是《Getting started with fp-ts: Category》,由Giulio Canti在2019年3月20日发表,并在2019年7月1日更新。文章是关于在fp-ts库中使用Category类型类的入门指南。

在之前的文章中,我们看到了函数式编程中使用的一些基本抽象:EqOrdSemigroupMonoid

在接下来的文章中,我们将探索一些使函数式编程更有趣的高级抽象。

从历史上看,fp-ts中第一个高级抽象是Functor,但在我们谈论函子之前,我们需要了解一些关于类别的知识,因为函子是建立在它们之上的。

函数式编程的一个基石是组合。但那到底意味着什么?我们什么时候可以说两个东西_组合_?我们什么时候可以说东西组合得_好_?

我们需要一个形式化的组合定义。这就是类别所涉及的。

类别捕捉了组合的本质。

类别

类别的定义有点长,所以我将把它的定义分成两部分:

  • 第一部分是技术性的(首先我们需要定义它的组成部分)

  • 第二部分将包含我们最感兴趣的内容:组合的概念

第一部分(定义)

一个类别是一对(Objects, Morphisms),其中:

  • Objects是一系列对象

  • Morphisms是一系列对象之间的态射(或箭头)

注意。这里的“对象”一词与OOP无关,你可以将对象视为无法检查的黑盒子,或者甚至是态射的一些辅助占位符。

每个态射f都有一个源对象A和一个目标对象B,其中AB都在Objects中。

我们写作f: A ⟼ B,并说“f是从A到B的态射”。

第二部分(组合)

有一个操作,名为“组合”,必须满足以下属性

  • 态射的组合)每当f: A ⟼ Bg: B ⟼ CMorphisms中的两个态射时,必须存在第三个态射g ∘ f: A ⟼ CMorphisms中,这是fg的_组合_

  • 结合性)如果f: A ⟼ Bg: B ⟼ Ch: C ⟼ D,那么h ∘ (g ∘ f) = (h ∘ g) ∘ f

  • 恒等式)对于每个对象X,存在一个态射identity: X ⟼ X称为X的_恒等态射_,使得对于每个态射f: A ⟼ X和每个态射g: X ⟼ B,我们有identity ∘ f = fg ∘ identity = g

示例

(来源:[wikipedia.org上的类别](https://en.wikipedia.org/wiki/Category_(mathematics)

这个类别非常简单,只有三个对象和六个态射(1A、1B、1C分别是ABC的恒等态射)。

类别作为编程语言

一个类别可以被解释为一个类型化编程语言的简化模型,其中:

  • 对象是类型

  • 态射是函数

  • 是通常的函数组合

这个图

可以被解释为一个相当简单的、想象的编程语言,只有三种类型和一小撮函数。

例如:

  • A = string

  • B = number

  • C = boolean

  • f = string => number

  • g = number => boolean

  • g ∘ f = string => boolean

实现可能是这样的:

function f(s: string): number {
  return s.length
}

function g(n: number): boolean {
  return n > 2
}

// h = g ∘ f
function h(s: string): boolean {
  return g(f(s))
}

TypeScript的类别

我们可以定义一个名为_TS_的类别,作为TypeScript语言的模型,其中:

  • 对象是所有TypeScript类型:stringnumberArray<string>,...

  • 态射是所有TypeScript函数:(a: A) => B(b: B) => C,...其中ABC,...是TypeScript类型

  • 恒等态射全部编码为一个多态函数const identity = <A>(a: A): A => a

  • 态射的组合是通常的函数组合(它是结合的)

作为TypeScript的模型,_TS_可能看起来太有限了:没有循环,没有if,几乎没有什么...尽管如此,这个简化的模型对于我们的主要目的来说已经足够丰富了:对一个定义良好的组合概念进行推理。

组合的核心问题

在_TS_中,我们可以组合两个通用函数f: (a: A) => Bg: (c: C) => D,只要B = C

function compose<A, B, C>(g: (b: B) => C, f: (a: A) => B): (a: A) => C {
  return a => g(f(a))
}

但如果B != C呢?我们如何_组合_这样的函数?我们是不是应该就此放弃?

在下一篇文章中,我们将看到在什么条件下这样的组合是可能的。我们将谈论函子

TLDR:函数式编程就是关于组合。


评论