使用无法隐式转换的参数定义方法

时间:2015-08-19 04:27:36

标签: scala

我尝试定义一个方法,该方法接受一个必须是A 类型的参数而没有隐式转换。

scala> def f[A](x: A =:= A) = x
f: [A](x: =:=[A,A])=:=[A,A]

为了说明我的观点,以下是不可接受的:

scala> implicit def stringToInt(x: String) = x.toInt
warning: there was one feature warning; re-run with -feature for details
stringToInt: (x: String)Int

scala> def g(x: Int) = x
g: (x: Int)Int

scala> g("5")
res5: Int = 5

但是,当我尝试拨打f时,我收到错误:

scala> f(100)
<console>:15: error: type mismatch;
 found   : Int(100)
 required: =:=[?,?]
       f(100)
         ^

如何更正f

3 个答案:

答案 0 :(得分:4)

正如炖菜所指出的,您对=:=的使用不正确。

以下是您尝试执行此操作的正确方法:

def g[A](x: A)(implicit e: A =:= Int) = x

像这样声明g(在更明显的def g(x: Int) = x上)的优点(在你的情况下)是参数是A类型的检查最后完成(通过在x参数已被类型检查后,查找适当的隐式值)。并且鉴于在类型检查x时,根本没有关于确切预期类型的​​信息,隐式转换无法启动:

scala> g("123")
<console>:13: error: Cannot prove that String =:= Int.
              g("123")
           ^
scala> g(123)
res1: Int = 123

但是,请注意(如Sascha Kolberg已经提到的),调用者仍然可以明确指定A,允许隐式转换应用:

scala> g[Int]("123")
res1: Int = 123

scala> g("123":Int)
res2: Int = 123

答案 1 :(得分:2)

我不确定你的隐含转换论证的问题是什么,我的建议是与此达成和平,但这里有一个可能对你有用的选项:

您可以使用类型类模式来定义类型上下文:

trait TypeEvidence[A]

object TypeEvidence {
  implicit object IntEv extends TypeEvidence[Int]
  implicit object LongEv extends TypeEvidence[Long]
  implicit object FloatEv extends TypeEvidence[Float]
  implicit object DoubleEv extends TypeEvidence[Double]
}

然后,您可以将函数参数限制为可用的上下文:

object Foo {
  def getValue[A : TypeEvidence](a: A): Int = 1
}

现在,这个函数只接受scala编译器找到与上下文绑定匹配的隐式类型的参数。

implicit def s2i(in: String): Int = Integer.parseInt(in)

Foo.getValue(100)           | -> 1
Foo.getValue("100")         | -> compile error: could not find implicit value for evidence parameter

注意:如果您注释了类型,隐式转换仍将启动。

Foo.getValue[Int]("100")    | -> 1

答案 2 :(得分:1)

如果您有一个带有单个参数的泛型方法,除非您明确指定类型,否则将使用参数类型调用始终而不进行任何隐式转换,

def foo[A](x: A): A = x
val x = foo(1) // will be of type Int
val y = foo[Long](1) // will use Int => Long conversion and be of type Long

如果您的方法包含 两个 参数,则情况会有所不同。对于下面的方法,数字强制将由编译器完成,编译器将找到一个公共类型。

def same[A](x: A, y: A) = x == y
foo(1, 1L) // works. coercion from int to long
foo(1, spire.math.Rational(1)) // A will be inferred to Any

如果你不想允许数字强制或任何,一种方法是将这些类型分开,但要求证明它们是相同的,如下所示:

def same[A, B](x: A, y: B)(implicit ev: A =:= B) = x == y

这只有在x和y确实属于同一类型时才有效,忽略强制和隐式转换:

scala> same(1, 1L)
<console>:15: error: Cannot prove that Int =:= Long.
       same(1, 1L)

scala> implicit def stringToInt(x: String) = x.toInt
    stringToInt: (x: String)Int
scala> same("1", 1)
<console>:16: error: Cannot prove that String =:= Int.
       same("1", 1)
       ^

这可用于定义不允许强制转换或隐式转换的运算符:

scala> implicit class NoCoercionEq[T](val lhs: T) { def ===[U](rhs:U)(implicit ev: T =:= U) = lhs == rhs }
defined class NoCoercionEq

scala> 1 === 1
res9: Boolean = true

scala> 1L === 1
<console>:21: error: Cannot prove that Long =:= Int.
       1L === 1
          ^