从某种意义上说,是的,隐式表示全局状态。但是,它们不是可变的,这是全局变量的真正问题-您不会看到人们抱怨全局常量,是吗?实际上,编码标准通常要求您将代码中的任何常量转换为通常是全局的常量或枚举。
还要注意,隐式不在平面名称空间中,这也是全局变量的常见问题。它们明确地与类型相关联,因此与这些类型的包层次结构相关联。
因此,使用您的全局变量,使它们在声明站点不变并初始化,然后将它们放在命名空间中。他们仍然看起来像全局变量吗?他们看起来仍然有问题吗?
但是,我们不要就此止步。Implicits被捆绑的类型,和他们一样多的“全球性”的类型。类型是全局的事实困扰您吗?
至于用例,有很多,但是我们可以根据它们的历史进行简要回顾。最初,Afaik,Scala没有隐式。Scala拥有的是视图类型,这是许多其他语言所具有的功能。今天,我们仍然可以看到,只要您编写类似的内容T <% Ordered[T]
,T
就可以将类型视为类型Ordered[T]
。视图类型是一种对类型参数(泛型)进行自动强制转换的方法。
然后,Scala用隐式泛化了该功能。自动转换不再存在,而是具有隐式转换-这些转换只是Function1
值,因此可以作为参数传递。从那时起,T <% Ordered[T]
意味着隐式转换的值将作为参数传递。由于强制转换是自动进行的,因此不需要函数的调用者显式传递参数-因此这些参数成为隐式参数。
请注意,有两个概念-隐式转换和隐式参数-非常接近,但并不完全重叠。
无论如何,视图类型成为隐式转换隐式传递的语法糖。它们将被这样重写:
def max[T <% Ordered[T]](a: T, b: T): T = if (a < b) b else a
def max[T](a: T, b: T)(implicit $ev1: Function1[T, Ordered[T]]): T = if ($ev1(a) < b) b else a
隐式参数只是该模式的一般化,因此可以传递任何种类的隐式参数,而不是just Function1
。然后是它们的实际用法,后来是这些用法的语法糖。
其中之一是Context Bounds,用于实现类型类模式(该模式不是内置功能,而是一种使用与Haskell的类型类提供类似功能的语言的方式)。上下文绑定用于提供适配器,该适配器实现类中固有的功能,但不是由类声明的。它提供了继承和接口的优点,而没有缺点。例如:
def max[T](a: T, b: T)(implicit $ev1: Ordering[T]): T = if ($ev1.lt(a, b)) b else a
def max[T: Ordering](a: T, b: T): T = if (implicitly[Ordering[T]].lt(a, b)) b else a
您可能已经使用过了-人们通常不会注意到一个常见的用例。它是这个:
new Array[Int](size)
它使用类清单的上下文绑定来启用此类数组初始化。通过以下示例可以看到:
def f[T](size: Int) = new Array[T](size)
您可以这样写:
def f[T: ClassManifest](size: Int) = new Array[T](size)
在标准库上,最常用的上下文边界是:
Manifest
ClassManifest
Ordering
Numeric
CanBuildFrom
后三个大多与集合使用,方法如max
,sum
和map
。Scalaz是一种广泛使用上下文范围的库。
另一个常见用法是减少必须共享一个公共参数的操作的样板。例如,交易:
def withTransaction(f: Transaction => Unit) = {
val txn = new Transaction
try { f(txn); txn.commit() }
catch { case ex => txn.rollback(); throw ex }
}
withTransaction { txn =>
op1(data)(txn)
op2(data)(txn)
op3(data)(txn)
}
然后将其简化为:
withTransaction { implicit txn =>
op1(data)
op2(data)
op3(data)
}
这种模式与事务性内存一起使用,我认为(但不确定)Scala I / O库也使用它。
我可以想到的第三个常见用法是对要传递的类型进行证明,这使在编译时检测可能导致运行时异常的事情成为可能。例如,请参阅以下定义Option
:
def flatten[B](implicit ev: A <:< Option[B]): Option[B]
这使得这成为可能:
scala> Option(Option(2)).flatten
res0: Option[Int] = Some(2)
scala> Option(2).flatten
<console>:8: error: Cannot prove that Int <:< Option[B].
Option(2).flatten
^
大量使用该功能的库是Shapeless。
我认为Akka库的示例不适合这四个类别中的任何一个,但这就是通用功能的全部要点:人们可以以各种方式使用它,而不是由语言设计师规定的方式使用。
如果您喜欢被要求开处方(例如Python这样做),那么Scala并不适合您。