为什么Haskell中没有很多关于共同和反差的讨论(与Scala或C#相反)?

Eri*_*ikR 56 haskell covariance contravariance

我知道类型的协方差和逆变.我的问题是为什么我在Haskell的研究中没有遇到过这些概念的讨论(与Scala相反)?

看起来Haskell查看类型的方式与Scala或C#相比存在根本区别,我想阐明这种区别是什么.

或者也许我错了,我还没有学到足够的Haskell :-)

C. *_*ann 60

主要有两个原因:

  • Haskell缺乏子类型的固有概念,因此通常方差不太相关.
  • 逆变性主要出现在涉及可变性的地方,因此Haskell中的大多数数据类型只是协变,并且明确区分它几乎没有价值.

然而,这些概念做应用-例如,通过进行起重作业fmapFunctor情况下,实际上是协变; 术语共同/逆变在类别理论中用于谈论仿函数.该contravariant为逆变函子定义了一个类型类,如果你查看实例列表,你会明白为什么我说它不太常见.

还有一些地方的想法隐含显示出来,在手动转换如何工作-各种数字型类定义的转换和从基本类型,如IntegerRational,模块Data.List包含的一些标准功能的通用版本.如果查看这些泛型版本的类型,您会看到Integral约束(赋予toInteger)用于逆变位置的类型,而Num约束(赋值fromInteger)用于协变位置.

  • @DanielC.Sobral:写一个可变引用是逆变的,是函数输入的一个特例.因此,虽然简单的数据类型通常允许协方差,但允许逆变的类型倾向于表示汇或输出,除非涉及某种副作用,否则这些类型可能是微不足道的.但是,具有读写操作的可变引用必然是不变的. (14认同)
  • 我认为逆变和可变性之间没有关系.实际上,可变性会导致不变性.逆变的例子是函数输入,`Ord`和`Eq`(当然它们的等价物),其中没有一个甚至有数据要变异. (7认同)
  • 我从来没有注意到`Num`和`Integral`是如此的双重! (6认同)
  • @CAMcCann这里有一些混淆的术语.你说*"允许逆变的类型往往代表汇或输出"* - 就像函数输入一样.使用*"接收器或输出"*和*"功能输入"*似乎是矛盾的.据我所知,函数输入是逆变的,函数输出是协变的.我相信你试图说函数输入就像汇入函数一样.因此它是函数的输出(这意味着它是一个输入).我对么?对不起,我非常困惑. (3认同)
  • @AaditMShah:对于这两个概念你是对的,这对我来说是一个不幸的选择.:]当调用一个函数时,它的参数是输出,但是它们输入的函数体.这里的问题是*调用者与被调用者的观点*,并且它们之间的切换反转所有差异.在上面,我使用"输入"来表示"参数",而不是从调用者的角度谈论,因此混淆.这同样适用于(具有相反的方差)函数返回值,它们实际上是"输出到调用者的连续". (3认同)

dfl*_*str 21

Haskell中没有"子类型",所以协方差和逆变没有任何意义.

在Scala中,您有例如Option[+A]子类Some[+A]None.你必须提供协方差注释+来表明a Option[Foo]是一个Option[Bar]if Foo extends Bar.由于存在子类型,这是必要的.

在Haskell中,没有子类型.OptionHaskell中的等价物称为Maybe:

data Maybe a = Nothing | Just a
Run Code Online (Sandbox Code Playgroud)

类型变量a只能是一种类型,因此不需要有关它的更多信息.


Han*_*ans 7

如上所述,Haskell没有子类型.但是,如果您正在查看类型类,可能不清楚如何在没有子类型的情况下工作.

类型类指定谓词,而不是类型本身.因此,当Typeclass有一个超类(例如Eq a => Ord a)时,这并不意味着实例是子类型,因为只有谓词是继承的,而不是类型本身.

此外,共同,反对和方差在不同的数学领域意味着不同的东西(见维基百科).例如,covariant和contravariant这两个术语用于仿函数(后者又用于Haskell),但这些术语意味着完全不同的东西.术语不变量可以在很多地方使用.