- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我想知道是否有人可以解释下面这个特殊情况下的推理规则,最重要的是它是有理/蕴涵的?
case class E[A, B](a: A) // class E
E(2) // E[Int,Nothing] = E(2)
请注意,我可以编写 E[Int](2)
。对我来说重要的是为什么第二个参数类型被推断为 Nothing
(即 Bottom 类型)而不是例如 Any
?为什么会这样?有什么道理/含义?
只是为了提供一些上下文,这与 Either 的定义及其对 Left 和 Right 的工作方式有关。两者都是根据模式定义的
final case class X[+A, +B](value: A) extends Either[A, B]
在实例化它的地方假设为 Right[Int](2)
并且推断的类型是 Right[Nothing, Int]
并且通过扩展 Either[什么都没有,Int]
这里有一致性,但我仍然可以找出合理性。下面是带有逆变参数的相同定义:
case class E[A, -B](a: A)// class E
E(2) // E[Int, Any] = E(2)
因此,当它是逆变时,我们确实有相同的东西,并且使所有行为或推理规则一致。但是我不确定这样做的合理性....
为什么不使用相反的规则,即在 Co-Variant/Invariant 时推断Any
,在 Contra-Variant 时推断Nothing
?
根据@slouc Answer,这很有道理,我仍然理解编译器正在做什么以及为什么这样做。下面的例子说明了我的困惑
val myleft = Left("Error") // Left[String,Nothing] = Left(Error)
myleft map { (e:Int) => e * 4} // Either[String,Int] = Left(Error)
Left[String,没有]
myleft
为 Either[String,Int] 类型给定 map 定义 def map[B](f: A => B): Either[E, B]
, (e:Int) => e * 4
只有在 myleft
实际上是 Left[String,Int]
或 Either[String,Int]
所以换句话说,我的问题是,如果以后要更改类型,那么将类型固定为 Nothing
有什么意义。
确实下面的不编译
val aleft: Left[String, Nothing] = Left[String, Int]("Error")
type mismatch;
found : scala.util.Left[String,Int]
required: Left[String,Nothing]
val aleft: Left[String, Nothing] = Left[String, Int]("Error")
那么为什么我要推断一个类型,这通常会阻止我对该类型的变量做任何其他事情(但肯定在推断方面有效),最终改变该类型,所以我可以使用该推断类型的变量做一些事情。
Edit2 有点误会,一切都在@slouc 的回答和评论中澄清。
最佳答案
协方差:
给定类型 F[+A]
和关系A <: B
,则以下成立:F[A] <: F[B]
逆变:
给定类型 F[-A]
和关系A <: B
,则以下成立:F[A] >: F[B]
如果编译器无法推断出确切的类型,它将在协变的情况下解析最低可能的类型,在逆变的情况下解析最高可能的类型。
为什么?
当涉及到子类型的变化时,这是一个非常重要的规则。它可以在 Scala 的以下数据类型的示例中显示:
trait Function1[Input-, Output+]
一般来说,当一个类型被放置在函数/方法参数中时,就意味着它处于所谓的“逆变位置”。如果它用在函数/方法返回值中,它就处于所谓的“协变位置”。如果两者都存在,则它是不变的。
现在,根据本文开头的规则,我们得出结论:
trait Food
trait Fruit extends Food
trait Apple extends Fruit
def foo(someFunction: Fruit => Fruit) = ???
我们可以供应
val f: Food => Apple = ???
foo(f)
函数f
是 someFunction
的有效替代品因为:
Food
是 Fruit
的父类(super class)型(输入的逆变)Apple
是 Fruit
的子类型(输出的协方差)我们可以这样用自然语言来解释:
"Method
foo
needs a function that can take aFruit
and produce aFruit
. This meansfoo
will have someFruit
and will need afunction it can feed it to, and expect someFruit
back. If it gets afunctionFood => Apple
, everything is fine - it can still feed itFruit
(because the function takes any food), and it can receiveFruit
(apples are fruit, so the contract is respected).
回到您最初的困境,希望这可以解释为什么在没有任何额外信息的情况下,编译器将求助于协变类型的最低可能类型和逆变类型的最高可能类型。如果我们想为 foo
提供一个函数,我们知道有一个确实有效:Any => Nothing
.
Variance in Scala documentation .
Article about variance in Scala (完全披露:我写的)。
编辑:
我想我知道是什么让您感到困惑。
当您实例化 Left[String, Nothing]
时, 你以后可以 map
它有一个功能 Int => Whatever
, 或 String => Whatever
, 或 Any => Whatever
.这恰恰是因为前面解释过的函数输入的逆变性。这就是为什么你的 map
有效。
"what is the point of fixing the type to Nothing if it is to change itlater?"
我认为编译器将未知类型修复为 Nothing
有点难在逆变的情况下。当它将未知类型修复为 Any
在协方差的情况下,它感觉更自然(它可以是“任何东西”)。由于前面解释的协变和逆变的对偶性,相同的推理适用于逆变 Nothing
和协变 Any
.
关于scala - 为什么 Scala 在未指定类型参数时推断底部类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64966219/
当使用模板模板参数时,我如何推断或删除模板模板的模板类型? 考虑以下 SSCCE: #include #include #include using namespace std; templat
假设我有一些特质: trait A[T] { def foo: T } 一个扩展它的类: class B[T](t: T) extends A[T] { def foo = t } 以及父特征的子特征
一边玩-rectypes在某些时候选择 OCaml 我只是迷路了。 这个表达式几乎可以打字: # fun x -> x x;; - : ('a -> 'b as 'a) -> 'b = 但是这里 O
我正在编写一个类似 CRUD 的应用程序,并且通过主键进行大量查找(主键可以有不同的类型)。所以我定义了以下类型类: {-# LANGUAGE MultiParamTypeClasses #-} cl
我已经创建了关系 A 'is functional parent of' B并定义 'has functional parent'作为 'is functional parent of' 的倒数. '
给定一个使用 Kotlin 版本 1.3.61 和 JOOQ 版本 3.13.1 的系统,这样的方法会构建 union正常查询: val selectCommonPart = coalesce
考虑以下错误代码: fun x = if (null x) then 0 else (take 50 x) : (fun (drop 50 x)) 我注意到,我可以毫无问题地将它加载到
给定一个具有以下类型的函数 a: a::x -> Bool 和以下类型的另一个函数 b: b::Bool -> y 我正在尝试找出推断以下函数类型的步骤: c =\d -> d a b 有人可以帮助解
我正在尝试使用 Infer 工具来分析我的应用代码。我关注了these steps每次我尝试运行 infer -- gradle build 时,我都会收到以下错误: infer -- gradle
所以我制作了这个模板来定义内联仿函数: template struct AsFunctor { template std::invoke_result_t operator()(A
是否可以推断 CRTP 基类中模板化成员函数的返回类型? 虽然推断参数类型效果很好,但它因返回类型而失败。考虑以下示例。 #include template struct base { tem
使用 Series.interpolate 很容易在 Pandas.DataFrame 中插入值,如何进行外推? 例如,给定一个如图所示的 DataFrame,我们如何将它外推 14 个月到 2014
我想知道为什么这不起作用(缺少参数类型)? Seq(1,2,3).toSet.map(_ + 1) 但这确实: val foo = Seq(1,2,3).toSet foo.map(_ + 1)
我没有必要使用 SQLite3 shell 工具来维护一个小型数据库。我正在使用 -header -ascii标志,尽管据我所知,这适用于任何输出选择。我正在寻找一种方法来避免对返回的任何一个值的类型
我有以下组件 type PropTypes = { items: T[], header: (item: T) => React.Element, body: (item: T) => R
我想在 Eclipse/JSDT 中指定实例变量的类型,如下例所示: /** * @constructor */ function A() { /** @type Node */
我正在用 Python 编写一个方法,它看起来像这样: def rgb_to_grayscale(image): print(image.shape) pass 此处预期的类型是 nu
我有一个 my_values 数组,我正在尝试为其推断 true_values 数组中最接近、较小的值。使用下面的 find_nearest 函数并不能完成我想要的。我如何追加它以找到最近的、较小的值
在下面的代码中: template int b(int q, const std::array& types) { int r = q; for (int t : types)
在 Pandas DataFrame 中插入 NaN 单元非常容易: In [98]: df Out[98]: neg neu pos av
我是一名优秀的程序员,十分优秀!