Scala 中隐式参数的好例子?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/9530893/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Good example of implicit parameter in Scala?
提问by greenoldman
So far implicit parameters in Scala do not look good for me -- it is too close to global variables, however since Scala seems like rather strict language I start doubting in my own opinion :-).
到目前为止,Scala 中的隐式参数对我来说并不好看——它太接近全局变量了,但是由于 Scala 似乎是一种相当严格的语言,我开始怀疑我自己的观点:-)。
Question:could you show a real-life (or close) good example when implicit parameters really work. IOW: something more serious than showPrompt, that would justify such language design.
问题:当隐式参数真正起作用时,您能否展示一个真实(或接近)的好例子。IOW:比 更严重的事情showPrompt,这将证明这种语言设计是合理的。
Or contrary -- could you show reliable language design (can be imaginary) that would make implicit not neccessary. I think that even no mechanism is better than implicits because code is clearer and there is no guessing.
或者相反 - 您能否展示可靠的语言设计(可以是想象的),这将使隐式变得不必要。我认为即使没有机制比隐式更好,因为代码更清晰,没有猜测。
Please note, I am asking about parameters, not implicit functions (conversions)!
请注意,我问的是参数,而不是隐式函数(转换)!
Updates
更新
Global variables
全局变量
Thank you for all great answers. Maybe I clarify my "global variables" objection. Consider such function:
谢谢你所有的好答案。也许我澄清了我的“全局变量”反对意见。考虑这样的函数:
max(x : Int,y : Int) : Int
you call it
你叫它
max(5,6);
you could (!) do it like this:
你可以(!)这样做:
max(x:5,y:6);
but in my eyes implicitsworks like this:
但在我眼里implicits是这样的:
x = 5;
y = 6;
max()
it is not very different from such construct (PHP-like)
它与这种构造(类似 PHP)没有太大区别
max() : Int
{
global x : Int;
global y : Int;
...
}
Derek's answer
德里克的回答
This is great example, however if you can think of as flexible usage of sending message not using implicitplease post an counter-example. I am really curious about purity in language design ;-).
这是一个很好的例子,但是如果你可以认为发送消息不使用的灵活用法,implicit请发布一个反例。我真的很好奇语言设计的纯度;-)。
回答by Daniel C. Sobral
In a sense, yes, implicits represent global state. However, they are not mutable, which is the true problem with global variables -- you don't see people complaining about global constants, do you? In fact, coding standards usually dictate that you transform any constants in your code into constants or enums, which are usually global.
从某种意义上说,是的,隐式表示全局状态。然而,它们不是可变的,这就是全局变量的真正问题——你不会看到人们抱怨全局常量,是吗?事实上,编码标准通常要求您将代码中的任何常量转换为常量或枚举,它们通常是全局的。
Note also that implicits are notin a flat namespace, which is also a common problem with globals. They are explicitly tied to types and, therefore, to the package hierarchy of those types.
另请注意,隐式不在平面命名空间中,这也是全局变量的常见问题。它们明确绑定到类型,因此绑定到这些类型的包层次结构。
So, take your globals, make them immutable and initialized at the declaration site, and put them on namespaces. Do they still look like globals? Do they still look problematic?
因此,使用您的全局变量,使它们不可变并在声明站点初始化,并将它们放在名称空间中。它们看起来仍然像全局变量吗?它们看起来仍然有问题吗?
But let's not stop there. Implicits aretied to types, and they are just as much "global" as types are. Does the fact that types are global bother you?
但我们不要止步于此。Implicits被捆绑的类型,和他们一样多的“全球性”的类型。类型是全局的这一事实是否困扰着您?
As for use cases, they are many, but we can do a brief review based on their history. Originally, afaik, Scala did not have implicits. What Scala had were view types, a feature many other languages had. We can still see that today whenever you write something like T <% Ordered[T], which means the type Tcan be viewed as a type Ordered[T]. View types are a way of making automatic casts available on type parameters (generics).
至于用例,它们很多,但我们可以根据它们的历史进行简要回顾。最初,afaik,Scala 没有隐式。Scala 拥有的是视图类型,这是许多其他语言都有的特性。今天我们仍然可以看到,每当你写类似的东西时T <% Ordered[T],这意味着类型T可以被视为一个类型Ordered[T]。视图类型是一种在类型参数(泛型)上进行自动转换的方法。
Scala then generalizedthat feature with implicits. Automatic casts no longer exist, and, instead, you have implicit conversions-- which are just Function1values and, therefore, can be passed as parameters. From then on, T <% Ordered[T]meant a value for an implicit conversion would be passed as parameter. Since the cast is automatic, the caller of the function is not required to explicitly pass the parameter -- so those parameters became implicit parameters.
Scala 然后用隐式概括了该特性。自动强制转换不再存在,取而代之的是隐式转换——它们只是Function1值,因此可以作为参数传递。从那时起,T <% Ordered[T]意味着隐式转换的值将作为参数传递。由于强制转换是自动的,函数的调用者不需要显式传递参数——因此这些参数变成了隐式参数。
Note that there are two concepts -- implicit conversions and implicit parameters -- that are very close, but do not completely overlap.
请注意,有两个概念——隐式转换和隐式参数——非常接近,但并不完全重叠。
Anyway, view types became syntactic sugar for implicit conversions being passed implicitly. They would be rewritten like this:
无论如何,视图类型成为隐式传递隐式转换的语法糖。它们将被重写如下:
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
The implicit parameters are simply a generalization of that pattern, making it possible to pass anykind of implicit parameters, instead of just Function1. Actual use for them then followed, and syntactic sugar for thoseuses came latter.
隐式参数只是该模式的概括,使得可以传递任何类型的隐式参数,而不仅仅是Function1. 然后是它们的实际使用,然后是这些用途的语法糖。
One of them is Context Bounds, used to implement the type class pattern(pattern because it is not a built-in feature, just a way of using the language that provides similar functionality to Haskell's type class). A context bound is used to provide an adapter that implements functionality that is inherent in a class, but not declared by it. It offers the benefits of inheritance and interfaces without their drawbacks. For example:
其中之一是Context Bounds,用于实现类型类模式(模式因为它不是内置功能,只是一种使用语言的方式,该语言提供与 Haskell 的类型类类似的功能)。上下文绑定用于提供一个适配器,该适配器实现类中固有但未由类声明的功能。它提供了继承和接口的优点而没有它们的缺点。例如:
def max[T](a: T, b: T)(implicit $ev1: Ordering[T]): T = if ($ev1.lt(a, b)) b else a
// latter followed by the syntactic sugar
def max[T: Ordering](a: T, b: T): T = if (implicitly[Ordering[T]].lt(a, b)) b else a
You have probably used that already -- there's one common use case that people usually don't notice. It is this:
您可能已经使用过它——人们通常不会注意到一个常见的用例。它是这个:
new Array[Int](size)
That uses a context bound of a class manifests, to enable such array initialization. We can see that with this example:
使用类清单的上下文绑定来启用此类数组初始化。我们可以通过这个例子看到:
def f[T](size: Int) = new Array[T](size) // won't compile!
You can write it like this:
你可以这样写:
def f[T: ClassManifest](size: Int) = new Array[T](size)
On the standard library, the context bounds most used are:
在标准库中,最常用的上下文边界是:
Manifest // Provides reflection on a type
ClassManifest // Provides reflection on a type after erasure
Ordering // Total ordering of elements
Numeric // Basic arithmetic of elements
CanBuildFrom // Collection creation
The latter three are mostly used with collections, with methods such as max, sumand map. One library that makes extensive use of context bounds is Scalaz.
后三个主要与集合一起使用,具有诸如max,sum和 之类的方法map。一个广泛使用上下文边界的库是 Scalaz。
Another common usage is to decrease boiler-plate on operations that must share a common parameter. For example, transactions:
另一个常见用法是减少必须共享公共参数的操作的样板。例如交易:
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)
}
Which is then simplified like this:
然后简化如下:
withTransaction { implicit txn =>
op1(data)
op2(data)
op3(data)
}
This pattern is used with transactional memory, and I think (but I'm not sure) that the Scala I/O library uses it as well.
这种模式与事务内存一起使用,我认为(但我不确定)Scala I/O 库也使用它。
The third common usage I can think of is making proofs about the types that are being passed, which makes it possible to detect at compile time things that would, otherwise, result in run time exceptions. For example, see this definition on Option:
我能想到的第三个常见用法是对正在传递的类型进行证明,这使得在编译时检测可能会导致运行时异常的事情成为可能。例如,请参阅以下定义Option:
def flatten[B](implicit ev: A <:< Option[B]): Option[B]
That makes this possible:
这使得这成为可能:
scala> Option(Option(2)).flatten // compiles
res0: Option[Int] = Some(2)
scala> Option(2).flatten // does not compile!
<console>:8: error: Cannot prove that Int <:< Option[B].
Option(2).flatten // does not compile!
^
One library that makes extensive use of that feature is Shapeless.
一个广泛使用该功能的库是 Shapeless。
I don't think the example of the Akka library fits in any of these four categories, but that's the whole point of generic features: people can use it in all sorts of way, instead of ways prescribed by the language designer.
我认为 Akka 库的例子不适合这四个类别中的任何一个,但这就是通用特性的全部意义:人们可以以各种方式使用它,而不是语言设计者规定的方式。
If you like being prescribed to (like, say, Python does), then Scala is just not for you.
如果你喜欢被规定(比如 Python 喜欢),那么 Scala 不适合你。
回答by Derek Wyatt
Sure. Akka's got a great example of it with respect to its Actors. When you're inside an Actor's receivemethod, you might want to send a message to another Actor. When you do this, Akka will bundle (by default) the current Actor as the senderof the message, like this:
当然。Akka 在 Actors 方面有一个很好的例子。当您在 Actor 的receive方法中时,您可能想要向另一个 Actor 发送消息。执行此操作时,Akka 将(默认情况下)将当前 Actor 捆绑为sender消息的 ,如下所示:
trait ScalaActorRef { this: ActorRef =>
...
def !(message: Any)(implicit sender: ActorRef = null): Unit
...
}
The senderis implicit. In the Actor there is a definition that looks like:
的sender是隐含的。在 Actor 中有一个定义,如下所示:
trait Actor {
...
implicit val self = context.self
...
}
This creates the implicit value within the scope of your own code, and it allows you to do easy things like this:
这会在您自己的代码范围内创建隐式值,并允许您执行以下简单的操作:
someOtherActor ! SomeMessage
Now, you can do this as well, if you like:
现在,如果您愿意,您也可以这样做:
someOtherActor.!(SomeMessage)(self)
or
或者
someOtherActor.!(SomeMessage)(null)
or
或者
someOtherActor.!(SomeMessage)(anotherActorAltogether)
But normally you don't. You just keep the natural usage that's made possible by the implicit value definition in the Actor trait. There are about a million other examples. The collection classes are a huge one. Try wandering around any non-trivial Scala library and you'll find a truckload.
但通常你不会。您只需保留由 Actor 特征中的隐式值定义实现的自然用法。还有大约一百万个其他例子。收集类是一个巨大的类。尝试在任何非平凡的 Scala 库中闲逛,您会发现一卡车的货物。
回答by Debilski
One example would be the comparison operations on Traversable[A]. E.g. maxor sort:
一个例子是对 的比较操作Traversable[A]。例如max或sort:
def max[B >: A](implicit cmp: Ordering[B]) : A
These can only be sensibly defined when there is an operation <on A. So, without implicits we'd have to supply the context Ordering[B]every time we'd like to use this function. (Or give up type static checking inside maxand risk a runtime cast error.)
只有在<对进行操作时才能明智地定义这些A。因此,如果没有隐式,我们Ordering[B]每次想使用这个函数时都必须提供上下文。(或者放弃内部类型静态检查max并冒运行时转换错误的风险。)
If however, an implicit comparison type classis in scope, e.g. some Ordering[Int], we can just use it right away or simply change the comparison method by supplying some other value for the implicit parameter.
但是,如果隐式比较类型类在范围内,例如 some Ordering[Int],我们可以立即使用它,或者通过为隐式参数提供一些其他值来简单地更改比较方法。
Of course, implicits may be shadowed and thus there may be situations in which the actual implicit which is in scope is not clear enough. For simple uses of maxor sortit might indeed be sufficient to have a fixed ordering traiton Intand use some syntax to check whether this trait is available. But this would mean that there could be no add-on traits and every piece of code would have to use the traits which were originally defined.
当然,隐含可能会被遮蔽,因此可能存在范围内的实际隐含不够清楚的情况。对于简单的用途max或者sort它可能确实是足够有一个固定的排序trait上Int,并使用一些语法检查这种特质是否可用。但这意味着不能有附加特性,每段代码都必须使用最初定义的特性。
Addition:
Response to the global variablecomparison.
附加:
响应全局变量比较。
I think you're correct that in a code snipped like
我认为你在代码中是正确的
implicit val num = 2
implicit val item = "Orange"
def shopping(implicit num: Int, item: String) = {
"I'm buying "+num+" "+item+(if(num==1) "." else "s.")
}
scala> shopping
res: java.lang.String = I'm buying 2 Oranges.
it may smell of rotten and evil global variables. The crucial point, however, is that there may be only one implicit variable per typein scope. Your example with two Ints is not going to work.
它可能有腐烂和邪恶的全局变量的味道。然而,关键的一点是,作用域中的每种类型可能只有一个隐式变量。你的例子有两个Ints 是行不通的。
Also, this means that practically, implicit variables are employed only when there is a not necessarily unique yet distinct primary instance for a type. The selfreference of an actor is a good example for such a thing. The type class example is another example. There may be dozens of algebraic comparisons for any type but there is one which is special.
(On another level, the actual line numberin the code itself might also make for a good implicit variable as long as it uses a very distinctive type.)
此外,这意味着实际上,仅当类型的主实例不一定唯一但不同时才使用隐式变量。self演员的参考就是这种事情的一个很好的例子。类型类示例是另一个示例。任何类型都可能有数十种代数比较,但有一种比较特殊。(在另一个层面上,代码本身中的实际行号也可能是一个很好的隐式变量,只要它使用非常独特的类型。)
You normally don't use implicits for everyday types. And with specialised types (like Ordering[Int]) there is not too much risk in shadowing them.
您通常不会将implicits 用于日常类型。并且对于专门的类型(如Ordering[Int]),隐藏它们并没有太大的风险。
回答by noam
Based on my experience there is no real good example for use of implicits parameters or implicits conversion.
根据我的经验,没有使用隐式参数或隐式转换的真正好的例子。
The small benefit of using implicits (not needing to explicitly write a parameter or a type) is redundant in compare to the problems they create.
与它们造成的问题相比,使用隐式(不需要显式编写参数或类型)的小好处是多余的。
I am a developer for 15 years, and have been working with scala for the last 1.5 years.
我是 15 年的开发人员,过去 1.5 年一直在使用 Scala。
I have seen many times bugs that were caused by the developer not aware of the fact that implicits are used, and that a specific function actually return a different type that the one specified. Due to implicit conversion.
我已经多次看到由开发人员引起的错误,因为他们不知道使用了隐式,并且特定函数实际上返回了与指定函数不同的类型。由于隐式转换。
I also heard statements saying that if you don't like implicits, don't use them. This is not practical in the real world since many times external libraries are used, and a lot of them are using implicits, so your code using implicits, and you might not be aware of that. You can write a code that has either:
我还听到有人说如果你不喜欢隐式,就不要使用它们。这在现实世界中是不切实际的,因为多次使用外部库,而且其中很多都使用隐式,因此您的代码使用隐式,而您可能没有意识到这一点。您可以编写具有以下任一特征的代码:
import org.some.common.library.{TypeA, TypeB}
or:
或者:
import org.some.common.library._
Both codes will compile and run. But they will not always produce the same results since the second version imports implicits conversion that will make the code behave differently.
两个代码都将编译并运行。但它们不会总是产生相同的结果,因为第二个版本导入了隐式转换,这将使代码表现不同。
The 'bug' that is caused by this can occur a very long time after the code was written, in case some values that are affected by this conversion were not used originally.
由此引起的“错误”可能会在编写代码后很长时间内发生,以防受此转换影响的某些值最初未使用。
Once you encounter the bug, its not an easy task finding the cause. You have to do some deep investigation.
一旦遇到错误,找到原因就不是一件容易的事。你必须做一些深入的调查。
Even though you feel like an expert in scala once you have found the bug, and fixed it by changing an import statement, you actually wasted a lot of precious time.
尽管一旦发现错误并通过更改导入语句修复它,您就会觉得自己是 Scala 专家,但实际上您浪费了很多宝贵的时间。
Additional reasons why I generally against implicits are:
我通常反对隐式的其他原因是:
- They make the code hard to understand (there is less code, but you don't know what he is doing)
- Compilation time. scala code compiles much slower when implicits are used.
- In practice, it changes the language from statically typed, to dynamically typed. Its true that once following very strict coding guidelines you can avoid such situations, but in real world, its not always the case. Even using the IDE 'remove unused imports', can cause your code to still compile and run, but not the same as before you removed 'unused' imports.
- 他们使代码难以理解(代码较少,但你不知道他在做什么)
- 编译时间。使用隐式时,Scala 代码的编译速度要慢得多。
- 实际上,它将语言从静态类型更改为动态类型。确实,一旦遵循非常严格的编码准则,您就可以避免这种情况,但在现实世界中,情况并非总是如此。即使使用 IDE 的“删除未使用的导入”,也会导致您的代码仍然可以编译和运行,但与删除“未使用的”导入之前的情况不同。
There is no option to compile scala without implicits (if there is please correct me), and if there was an option, none of the common community scala libraries would have compile.
没有隐式编译 scala 的选项(如果有请纠正我),如果有选项,则没有一个常见的社区 scala 库会编译。
For all the above reasons, I think that implicits are one of the worst practices that scala language is using.
由于上述所有原因,我认为隐式是 Scala 语言使用的最糟糕的做法之一。
Scala has many great features, and many not so great.
Scala 有很多很棒的特性,但也有很多不那么好。
When choosing a language for a new project, implicits are one of the reasons against scala, not in favour of it. In my opinion.
在为新项目选择语言时,隐式是反对 Scala 的原因之一,而不是支持它。在我看来。
回答by samthebest
It's easy, just remember:
很简单,记住:
- to declare the variable to be passed in as implicit too
- to declare all the implicit params after the non-implicit params in a separate ()
- 将要传入的变量也声明为隐式
- 在单独的 () 中声明非隐式参数之后的所有隐式参数
e.g.
例如
def myFunction(): Int = {
implicit val y: Int = 33
implicit val z: Double = 3.3
functionWithImplicit("foo") // calls functionWithImplicit("foo")(y, z)
}
def functionWithImplicit(foo: String)(implicit x: Int, d: Double) = // blar blar
回答by Jean-Philippe Pellet
Another good general usage of implicit parameters is to make the return type of a method depend on the type of some of the parameters passed to it. A good example, mentioned by Jens, is the collections framework, and methods like map, whose full signature usually is:
隐式参数的另一个很好的通用用法是使方法的返回类型取决于传递给它的某些参数的类型。Jens 提到的一个很好的例子是集合框架和方法,如map,其完整签名通常是:
def map[B, That](f: (A) ? B)(implicit bf: CanBuildFrom[GenSeq[A], B, That]): That
Note that the return type Thatis determined by the best fitting CanBuildFromthat the compiler can find.
请注意,返回类型That由CanBuildFrom编译器可以找到的最佳拟合确定。
For another example of this, see that answer. There, the return type of the method Arithmetic.applyis determined according to a certain implicit parameter type (BiConverter).
有关此的另一个示例,请参阅该答案。在那里,方法的返回类型Arithmetic.apply是根据某个隐式参数类型 ( BiConverter) 确定的。
回答by Jens Schauder
Implicit parameters are heavily used in the collection API. Many functions get an implicit CanBuildFrom, which ensures that you get the 'best' result collection implementation.
隐式参数在集合 API 中被大量使用。许多函数获得隐式 CanBuildFrom,这确保您获得“最佳”结果集合实现。
Without implicits you would either pass such a thing all the time, which would make normal usage cumbersome. Or use less specialized collections which would be annoying because it would mean you loose performance/power.
如果没有隐式,你要么一直传递这样的东西,这会使正常使用变得麻烦。或者使用不太专业的集合,这会很烦人,因为这意味着你会失去性能/能力。
回答by anshuman sharma
I am commenting on this post a bit late, but I have started learning scala lately. Daniel and others have given nice background about implicit keyword. I would provide me two cents on implicit variable from practical usage perspective.
我评论这篇文章有点晚了,但我最近开始学习scala。Daniel 和其他人提供了有关隐式关键字的很好的背景知识。从实际使用的角度来看,我会为我提供两美分的隐式变量。
Scala is best suited if used for writing Apache Spark codes. In Spark, we do have spark context and most likely the configuration class that may fetch the configuration keys/values from a configuration file.
Scala 最适合用于编写 Apache Spark 代码。在 Spark 中,我们确实有 spark 上下文,并且很可能是可以从配置文件中获取配置键/值的配置类。
Now, If I have an abstract class and if I declare an object of configuration and spark context as follows :-
现在,如果我有一个抽象类,并且我声明了一个配置对象和 spark 上下文,如下所示:-
abstract class myImplicitClass {
implicit val config = new myConfigClass()
val conf = new SparkConf().setMaster().setAppName()
implicit val sc = new SparkContext(conf)
def overrideThisMethod(implicit sc: SparkContext, config: Config) : Unit
}
class MyClass extends myImplicitClass {
override def overrideThisMethod(implicit sc: SparkContext, config: Config){
/*I can provide here n number of methods where I can pass the sc and config
objects, what are implicit*/
def firstFn(firstParam: Int) (implicit sc: SparkContext, config: Config){
/*I can use "sc" and "config" as I wish: making rdd or getting data from cassandra, for e.g.*/
val myRdd = sc.parallelize(List("abc","123"))
}
def secondFn(firstParam: Int) (implicit sc: SparkContext, config: Config){
/*following are the ways we can use "sc" and "config" */
val keyspace = config.getString("keyspace")
val tableName = config.getString("table")
val hostName = config.getString("host")
val userName = config.getString("username")
val pswd = config.getString("password")
implicit val cassandraConnectorObj = CassandraConnector(....)
val cassandraRdd = sc.cassandraTable(keyspace, tableName)
}
}
}
As we can see the code above, I have two implicit objects in my abstract class, and I have passed those two implicit variables as function/method/definition implicit parameters. I think this is the best use case that we can depict in terms of usage of implicit variables.
正如我们在上面的代码中看到的那样,我的抽象类中有两个隐式对象,并且我已经将这两个隐式变量作为函数/方法/定义隐式参数传递了。我认为这是我们可以根据隐式变量的使用来描述的最佳用例。

