可否在Scala中使用传递隐式参数?

3

假设我有几个函数:

func1 : A => B

func2:  B => C

func3:  C => D

我希望现在能够以通用方式按需编排功能。
比如说,如果我需要将 A 转换为 B,我会调用 func1。 但是当我需要将 A 转换为 D 时,我想要这些函数的组合。这种动态方式是否可行?

考虑到隐式已经会影响编译时间并降低项目的可读性,从而降低在其上工作时的生产力,你想要的听起来像是一个非常糟糕的主意。 - Esko
1个回答

5
从Scala 语言特性文档中解释为什么在2.10中必须明确启用隐式转换:
“为什么要控制它?” 隐式转换如果被过度使用,就会导致许多陷阱。而且有一种倾向是过度使用它们,因为它们看起来非常强大,它们的影响似乎很容易理解。此外,在大多数情况下,使用隐式参数比使用隐式转换会导致更好的设计。
用户定义的隐式转换几乎总是一个坏主意,使它们具有传递性将会更糟糕得多。
但是,您可以使用类型类以更安全、更受控制的方式获得类似的效果。例如,假设我们有以下内容:
trait MyConverter[A, B] { def apply(a: A): B }

implicit def composeMyConverters[A, B, C](implicit
  ab: MyConverter[A, B],
  bc: MyConverter[B, C]
) = new MyConverter[A, C] { def apply(a: A) = bc(ab(a)) }

现在我们可以写成:
implicit object doubleToString extends MyConverter[Double, String] {
  def apply(d: Double) = d.toString
}

implicit object intToDouble extends MyConverter[Int, Double] {
  def apply(i: Int) = i.toDouble
}

def convertToString[A](a: A)(implicit as: MyConverter[A, String]) = as(a)

最后:

scala> convertToString(13: Int)
res0: String = 13.0

我们从未明确定义过从整数到字符串的转换器,但编译器能够使用我们的composeMyConverters方法在需要时构建一个转换器。
与隐式转换一样,这种方法也可能被滥用,但更容易跟踪转换器在哪里应用,以及它们在何处适用。

我喜欢你的方法Travis,但实际上你是在链接隐式转换使它们具有传递性吗? - Stefan Kunze
1
是的,但是链接是自动发生的,而且这些不是隐式转换 - 它们是类型类实例,由编译器召唤以满足隐式参数。 - Travis Brown
1
你能否详细说明一下这个差别,以及它为何重要? - Stefan Kunze
1
标准库中到处都是隐式转换,任何人都可以定义更多的转换,它们可能会应用在你意想不到的地方。在我的例子中,您已经定义了类型类,因此您知道可用的实例,并且转换不会自动应用 - 您必须使用我convertToString方法中看到的隐式参数方法。 - Travis Brown
嘿 Travis,我们刚刚尝试了更长的链式操作(A -> B -> C -> D),但我们得到了不一致的推导结果,这对我们来说是有道理的,因为函数组合是可结合的,所以不同的分组会导致多个实例。我怀疑我们无法教会scalac关于结合律的知识,所以我想知道是否有避免分歧的方法。希望答案不是宏。 - Sukant Hajra

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接