隐式转换作为参数有什么意义?
What's the point of implicit conversions as parameters?
我正在阅读 docs on implicits in Scala,其中有一个将隐式转换作为参数的函数示例:
def getIndex[T, CC](seq: CC, value: T)(implicit conv: CC => Seq[T]) = seq.indexOf(value)
我明白它是如何工作的,但我不明白这样写而不是:
有什么意义
def getIndexExplicit[T](seq: Seq[T], value: T) = seq.indexOf(value)
据我所知,如果存在从参数 seq
到类型 Seq[T]
的转换,编译器仍会允许调用 getIndexExplicit
?
为了说明我的观点,我准备了这个简单的例子:
def equal42[T](a: T)(implicit conv: T => Int) = conv(a) == 42 // implicit parameter version
def equal42Explicit(a: Int) = a == 42 // just use the type in the signature
implicit def strToInt(str: String): Int = java.lang.Integer.parseInt(str) // define the implicit conversion from String to Int
事实上,这两个功能似乎以相同的方式工作:
scala> equal42("42")
res12: Boolean = true
scala> equal42Explicit("42")
res13: Boolean = true
如果没有区别,显式定义隐式转换有什么意义?
我的猜测是,在这个简单的情况下,它没有什么区别,但一定有一些更复杂的场景。那些是什么?
在您的超级简单示例中:
equal42("42")
equal42Explicit("42")
等于
equal42("42")(strToInt)
equal42Explicit(strToInt("42"))
这对您的定义没有影响。
但是如果它做了其他事情,例如
def parseCombined[S, T](s1: S, s2: S)
(combine: (S, S) => S)
(implicit parse: S => Option[T]): Option[T] =
parse(combine(s1, s2))
那么您何时应用转换很重要:
implicit def lift[T]: T => Option[T] = t => Option(t)
implicit val parseInt: String => Option[Int] = s => scala.util.Try(s.toInt).toOption
implicit def parseToString[T]: T => Option[String] = t => Option(t.toString)
parseCombined[Option[Int], String]("1", "2") { (a, b) => a.zip(b).map { case (x, y) => x + y } } // Some("Some(3)")
parseCombined[String, Int]("1", "2") { _ + _ } // Some(12)
在第一种情况下,参数在传递之前被转换(然后在内部再次转换),而在另一种情况下,它们仅在特定位置的函数内部转换。
虽然这个案例有点牵强,但它表明控制何时进行转换可能对最终结果很重要。
据说这种隐式转换的用法是一种反模式,类型 类 会更好。实际上,扩展方法是隐式转换唯一没有争议的用法,因为即使是磁铁模式——唯一可能用于生产的其他用例(参见 Akka)——也可能被视为问题。
因此,请将文档中的这个示例视为一种机制的演示,而不是应在生产中使用的良好实践示例。
我正在阅读 docs on implicits in Scala,其中有一个将隐式转换作为参数的函数示例:
def getIndex[T, CC](seq: CC, value: T)(implicit conv: CC => Seq[T]) = seq.indexOf(value)
我明白它是如何工作的,但我不明白这样写而不是:
有什么意义def getIndexExplicit[T](seq: Seq[T], value: T) = seq.indexOf(value)
据我所知,如果存在从参数 seq
到类型 Seq[T]
的转换,编译器仍会允许调用 getIndexExplicit
?
为了说明我的观点,我准备了这个简单的例子:
def equal42[T](a: T)(implicit conv: T => Int) = conv(a) == 42 // implicit parameter version
def equal42Explicit(a: Int) = a == 42 // just use the type in the signature
implicit def strToInt(str: String): Int = java.lang.Integer.parseInt(str) // define the implicit conversion from String to Int
事实上,这两个功能似乎以相同的方式工作:
scala> equal42("42")
res12: Boolean = true
scala> equal42Explicit("42")
res13: Boolean = true
如果没有区别,显式定义隐式转换有什么意义?
我的猜测是,在这个简单的情况下,它没有什么区别,但一定有一些更复杂的场景。那些是什么?
在您的超级简单示例中:
equal42("42")
equal42Explicit("42")
等于
equal42("42")(strToInt)
equal42Explicit(strToInt("42"))
这对您的定义没有影响。
但是如果它做了其他事情,例如
def parseCombined[S, T](s1: S, s2: S)
(combine: (S, S) => S)
(implicit parse: S => Option[T]): Option[T] =
parse(combine(s1, s2))
那么您何时应用转换很重要:
implicit def lift[T]: T => Option[T] = t => Option(t)
implicit val parseInt: String => Option[Int] = s => scala.util.Try(s.toInt).toOption
implicit def parseToString[T]: T => Option[String] = t => Option(t.toString)
parseCombined[Option[Int], String]("1", "2") { (a, b) => a.zip(b).map { case (x, y) => x + y } } // Some("Some(3)")
parseCombined[String, Int]("1", "2") { _ + _ } // Some(12)
在第一种情况下,参数在传递之前被转换(然后在内部再次转换),而在另一种情况下,它们仅在特定位置的函数内部转换。
虽然这个案例有点牵强,但它表明控制何时进行转换可能对最终结果很重要。
据说这种隐式转换的用法是一种反模式,类型 类 会更好。实际上,扩展方法是隐式转换唯一没有争议的用法,因为即使是磁铁模式——唯一可能用于生产的其他用例(参见 Akka)——也可能被视为问题。
因此,请将文档中的这个示例视为一种机制的演示,而不是应在生产中使用的良好实践示例。