有选择地禁用Scala中的包含?(正确输入List.contains)

She*_*III 22 scala contains list covariance implicit-cast

List("a").contains(5)
Run Code Online (Sandbox Code Playgroud)

因为a Int永远不会包含在列表中String,所以这应该在编译时生成错误,但事实并非如此.

它浪费并默默地测试String列表中包含的每个内容的相等性5,这些内容永远不会成立(在Scala中"5"永远不等于5).

这被称为" '包含'问题 ".而一些人暗示,如果一个类型系统无法正确地输入这样的语义,那么为什么要经过强制执行类型的额外的努力.所以我认为这是一个需要解决的重要问题.

类型参数化B >: AList.contains输入的任何类型的是该类型的超类型A(包含在列表中的元素的类型).

trait List[+A] {
   def contains[B >: A](x: B): Boolean
}
Run Code Online (Sandbox Code Playgroud)

此类型参数化是必要的,因为+A声明列表在类型上是协变A,因此A不能在逆变位置中使用,即作为输入参数的类型.协变列表(必须是不可变的)对于扩展比强制列表(可以是可变的)更强大.

AString在上面的例子有问题,但Int不是的超类型String,所以发生了什么事?该隐含包容 Scala中,决定Any是两者的相互超StringInt.

Scala的创建者Martin Odersky 建议修复将限制输入类型B仅限于那些具有equals方法的类型Any.

trait List[+A] {
   def contains[B >: A : Eq](x: B): Boolean
}
Run Code Online (Sandbox Code Playgroud)

但这并没有解决问题,因为两种类型(其中输入类型不是列表元素类型的超类型)可能具有相互超类型,它是子类型Any,也是子类型Eq.因此,它将编译没有错误,并且将保留错误键入的语义.

在每个地方禁用隐式包含也不是理想的解决方案,因为隐式包含是下面的例子为什么包含Any工作的原因.当接收站点(例如作为函数参数传递)为相互超类型(甚至可能不是Any)输入正确的语义时,我们不希望被强制使用类型转换.

trait List[+A] {
   def ::[B >: A](x: B): List[B]
}

val x : List[Any] = List("a", 5) // see[1]
Run Code Online (Sandbox Code Playgroud)

[1] List.apply调用::运算符.

所以我的问题是这个问题的最佳解决方案是什么?

我的初步结论是,应该在定义站点关闭隐式包含,否则语义不能正确输入.我将提供一个答案,显示如何在方法定义站点关闭隐式包含.有替代解决方案吗?

请注意,此问题是一般性的,不会与列表隔离.

更新:我已经提交了一个改进请求,在此开始了一个scala讨论主题.我还在Kim Stebel和Peter Schmitz的回答中添加了评论,表明他们的答案有错误的功能.因此没有解决方案.同样在上述讨论主题中,我解释了为什么我认为soc的答案是不正确的.

soc*_*soc 12

这在理论上听起来不错,但在我看来在现实生活中分崩离析.

equals不是基于类型,contains而是建立在其之上.

这就是为什么代码1 == BigInt(1)工作并返回大多数人期望的结果的原因.

在我看来,制定contains更严格的条款是没有意义的equals.

如果contains会更严格,代码List[BigInt](1,2,3) contains 1就会完全停止工作.

顺便提一句,我认为"不安全"或"不安全"是正确的条款.


Kim*_*bel 8

为什么不使用相等的类型类?

scala> val l = List(1,2,3)
l: List[Int] = List(1, 2, 3)

scala> class EQ[A](a1:A) { def ===(a2:A) = a1 == a2 } 
defined class EQ

scala> implicit def toEQ[A](a1:A) = new EQ(a1)
toEQ: [A](a1: A)EQ[A]

scala> l exists (1===)
res7: Boolean = true

scala> l exists ("1"===)
<console>:14: error: type mismatch;
 found   : java.lang.String => Boolean
 required: Int => Boolean
              l exists ("1"===)
                           ^

scala> List("1","2")
res9: List[java.lang.String] = List(1, 2)

scala> res9 exists (1===)
<console>:14: error: type mismatch;
 found   : Int => Boolean
 required: java.lang.String => Boolean
              res9 exists (1===)
Run Code Online (Sandbox Code Playgroud)

  • 这个问题已经在13年前的Haskell中使用Eq类型类解决了。 (2认同)

Vla*_*hev 2

我认为我至少对这里发布的一些问题有一个合法的解决方案 - 我的意思是,问题是List("1").contains(1)https://docs.google.com/document/d/1sC42GKY7WvztXzgWPGDqFukZ0smZFmNnQksD_lJzm20/edit