据我所知,类型类不是具体的东西,而只是一个用于ad-hoc和参数多态的构造。Eq和半群是类型类的例子。另一方面,有代数数据类型,这是一个具体的复合类型,例如,Either和Maybe。它们也是函子。
因此,JavaScript有一个代数数据类型规范:https://github.com/fantasyland/fantasy-land。在本页中,Setoid(Eq)、Ord和半群也是ADT。但它是否正确?如果正确,它们是什么类型的复合?
我还找到了这篇关于类型类的文章,这里的Functor和Monad都是类型类。https://typelevel.org/cats/typeclasses.html#type-classes-in-cats。这是否意味着或者和也许也是类型类?
Eq是 * 类型类 * 还是 * 代数数据类型 *?还是两者都是?Functor相同
3条答案
按热度按时间pvabu6sv1#
我们将数据(值、项)收集到 * types *(数据类型)中,也可以将类型收集到 * type class * 中。
1
,"a"
,true
,Some(1)
,None
,...是值。它们属于类型Int
,String
,Boolean
,Option[Int]
,...。类型可以属于类型类Eq
,Ord
,Functor
,...ADT(代数数据类型)是通过求和和乘积构造的数据类型
其中
MyTrait
是MyClass1
与MyClass2
之和,MyClass1
是Int
与String
的乘积,MyClass2
是单个乘法器Boolean
的乘积。也有一些数据类型不是ADT。例如,函数类型
A => B
、联合类型A | B
、交集类型A & B
或A with B
、存在类型等。Scala ADT是自动泛化的ADT(GADT)。在依赖类型语言中有ADT的推广,Sigma-和Pi-类型(即依赖和与积)。类型类是一种描述行为的FP方式(通过adhoc多态性、早期绑定、静态/编译时调度)。我们可以使类型成为类型类的 * 示例 *
一个二个一个一个
这里
MyClass1
,MyClass2
,MyTrait
(在Haskell中只有MyTrait
)是类型,它们是类型类MyTypeclass
的示例。类型类的另一种选择(描述行为的另一种方式)是OOP继承(通过子类型多态性、后期绑定、动态/运行时分派)
我们可以将ADT "提升"到类型类中,例如标准ADT
可以成为类型类及其示例
在本页中,
Setoid
(Eq
)、Ord
和Semigroup
也是ADT,但是否正确?如果正确,它们是由哪些类型组成的?通常,
Eq
、Ord
、Semigroup
不是ADT。它们不是通过求和和乘积构造的。它们是类型类。它们描述行为,即如何比较元素的相等性、如何比较元素的顺序、如何添加元素、什么是单位元素。它们由声明为示例的所有类型组成,例如Int
。String
等(即,其中以某种特定方式实现对应的行为)。是否意味着
Either
和Maybe
也是类型类?通常,
Either
和Maybe
不是类型类,它们是ADT。但是如果我们真的需要,我们可以将ADT提升到类型类中,如我上面所示。Eq
是类型类还是代数数据类型?或者两者都是?Functor
相同。Eq
和Functor
是类型类,而不是ADT。kiz8lqtg2#
代数数据类型和类型类都是“具有多种类型的事物”,我认为这就是让你困惑的地方。我认为我们很难回答你的问题(甚至很难弄清楚它是什么)的原因是,除此之外,它们几乎没有任何共同之处。
类型类可以被看作是一个“契约”或“规范”,它独立于任何具体类型而存在,并且它们是“开放的”:并且你所想到的任何类型都可以实现必要的细节来成为集合的成员。我们在包含这个概念的不同语言中以不同的方式来指定它。
例如,生 rust 性状
Display
:或者像
Show
这样的Haskell类型类:在这两种情况下,我们都基于整数类型创建了一个非常基本的类型
Foo
,它在各自的语言中实现了“has a string representation”类型类,正如我之前所说的,实现Show/Display
的类型集是 open:我们可以用任何合适的类型永远这样做。与
Maybe
形成对比,Maybe
是一个标记的并集(它将其限定为代数数据类型)a
,Maybe
并集中的类型集是 * 封闭的 :你可以使用任何你想要的类型a
,但是你不能给联合体添加新的东西。Maybe
是一个 type(或者更准确地说,是一个类型构造函数),而不是一个 typeclass。说Foo
实现/派生Maybe
是没有意义的。Maybe
不是其他类型要遵循的规则集, 任何东西 * 都可以是Maybe Whatever
。但是
Maybe
作为一个类型确实 * 实现 * 了一些类型类,例如Monad
类型类(以及隐含的Functor
、Applicative
等)。5lhxktic3#
混淆的根本原因在于概念与实现的区别。
所以让我们试着把其中的一些弄清楚。
类型类
是实现多态的模式;准确地说是特别多态性。
在某些语言中,这是该语言提供的一个特征;例如Haskell或** rust eclipse 。
其他,允许您使用其他功能来表达它们;例如,Scala使用隐式,这意味着它们是该语言中的值,而不是Rust***(AFAIK)* 中的值
最后,在理论上,你可以用任何语言建模,但是你需要手动传递示例,这就是通常所说的策略模式;一个例子是Java的
Comparator
,有些人认为如果值必须显式传递,那么它就不是类型类,我个人不同意,但我们不要打开潘多拉的盒子。代数数据类型(ADT)
是一种基于简单构造函数(称为乘积和联合)对数据建模的模式。
同样,有些语言提供了开箱即用的构建块,如Haskell;还有一些语言在编译器的帮助下,在经典的子类型和对象上模拟它们,如Scala。
同样,理论上,您可以用任何语言对它们建模,并用
fold
替换模式匹配,但是,在这种情况下,我确实同意,如果UX不令人愉快,并且语言中没有内置的模式匹配,那么就很难谈论ADT。函数
是一个抽象概念,一个从范畴论中衍生出来的概念。
从本质上讲,它由三个部分组成:
* -> *
* 的类型F[_]
(通常称为类型构造函数)*;例如List
F
的map[A, B](fa: F[A])(f: A => B): F[B]
实现Functor
定律的形式证明。我不会试图在这里解释
Functor
是什么...但是,一个快速的TL; DR;表示通过将函数应用于它将计算的值来转换某些上下文的可能性。
由于
Functor
是一种抽象,我们需要某种形式的多态来表示它。经验告诉我们,类型类是对Functor
、Monad
、Monoid
等进行建模的最自然的方式。但是,您可以尝试使用其他方法,如结构类型或子类型......但实际上,您将很快遇到瓶颈。额外
除此之外,我们还需要添加每种语言可能具有的其他间接层。
例如,在Scala中,我们使用子类型来建模类型类层次结构,因此
Monad[F] extends Functor[F]
,并且使用相同的关键字来建模子类型抽象和类型类抽象trait
。因此,始终将概念/思想与其底层实现分离是很重要的;在一天结束的时候,一切都将只是一个奇怪的机器上的电脉冲:p