5

Similar to this Scala: Why does Seq.contains take an Any argument, instead of an argument of the sequence type?

If you do Seq(1, 2, 3).contains("dasdas")

This compiles and always returns false. Is there an alternative which throws a type error ? This seems to a be major wtf where the contains always returns false and is super easy to miss in code reviews.

Mario Galic
  • 47,285
  • 6
  • 56
  • 98

2 Answers2

7

You can enable -Xfatal-warnings and -Ywarn-infer-any to make it fail when there is type mismatch.
That also works for equality checks ==.

Here is an example of what I use in my build.sbt to avoid weird Scala like you are experiencing:

scalacOptions ++= Seq(
  "-deprecation",
  "-explaintypes",
  "-feature",
  "-language:higherKinds",
  "-unchecked",
  "-Xcheckinit",
  "-Xfatal-warnings",
  "-Xfuture",
  "-Xlint",
  "-Yno-adapted-args",
  "-Ypartial-unification",
  "-Ywarn-dead-code",
  "-Ywarn-inaccessible",
  "-Ywarn-infer-any",
  "-Ywarn-nullary-override",
  "-Ywarn-nullary-unit",
  "-Ywarn-numeric-widen",
  "-Ywarn-unused"
) ++ (
  if (scalaVersion.value.startsWith("2.11")) Seq.empty
  else Seq("-Ywarn-extra-implicit")

There is great article from Rob Norris on those:
https://tpolecat.github.io/2017/04/25/scalac-flags.html

FYI: Universal equality will be replaced by Multiversal equality in Scala 3 to address your issue:
http://dotty.epfl.ch/docs/reference/contextual/multiversal-equality.html

Joan
  • 4,079
  • 2
  • 28
  • 37
  • Thanks. For ref for others, the magic sauce is basically ` "-Ywarn-infer-any",` + ` "-Xfatal-warnings",` Need to understand how to pass it into Pants. Also yay for multiversal equality but boo for waiting till Scala 3.The blog is from 2016, could have easily made it to the language by now if they had just used `=== ` though ‍♂️ – Prakash Rajagaopal Jul 09 '19 at 03:13
  • I tried without `-Ywarn-infer-any` and it's not needed for this exact problem, but it's useful for other cases. The `===` is the wrong way of doing, think about it, it would be type safe for the Scala code that used `===` and inconsistant with the underlying Java code. It's not as easy as it sounds to have a type safe equality check that is consistant across the whole language without breakage. The implementation in Scala 3 via a type class is I think the most reasonable way of doing. – Joan Jul 09 '19 at 05:51
  • On Scala 2.12.7 and it doesn't seem to work with just fatal warnings though – Prakash Rajagaopal Jul 09 '19 at 06:38
5

You can write a extension method to only accept the defined type,

implicit class ListOps[A](list: List[A]) {
  def typeSafeContains(a: A) = list.contains(a)
}

List(1, 2, 3).typeSafeContains(1)
List(1, 2, 3).typeSafeContains("does not work") //type mismatch error;

Off-topic but I checked haskell has typesafe contains

Prelude> elem 1 [1,2,3]
True

Prelude> elem "should not work" [1,2,3]

<interactive>:6:25: error:
    • No instance for (Num [Char]) arising from the literal ‘1’
    • In the expression: 1
      In the second argument of ‘elem’, namely ‘[1, 2, 3]’
      In the expression: elem "should not work" [1, 2, 3]
prayagupa
  • 30,204
  • 14
  • 155
  • 192
  • Thanks, Definitely would work but probably near impossible to get this into all our codebase though + this needs to be defined for all other collections as well as for options though there should be some variance magic to get through. – Prakash Rajagaopal Jul 09 '19 at 03:07