scala 隐式转换_Scala期刊揭穿了隐式秘密的神秘面纱

scala 隐式转换

Today I will cover quite an interesting topic — implicits. They are almost as interesting as they are infamous for adding to codebase complexity and debugging hell stories (Spoiler alert: as you will find out soon — only if they are used incorrectly).

今天,我将介绍一个非常有趣的主题- 隐式。 它们几乎与臭名昭著的臭名昭著,因为它们增加了代码库的复杂性并调试了地狱故事(剧透警报:您很快就会发现-仅在错误使用它们的情况下)。

So let’s try and find out what are they, why are they needed and most importantly — when should we use them in real life? As it turns out again… once you grasp the basics they are not as terrifying as they are made to be.

因此,让我们尝试找出它们是什么 ,为什么需要它们,最重要的是-我们何时应在现实生活中使用它们? 事实再次证明……一旦您掌握了基础知识,它们就不会像想象的那样可怕。

什么是隐式 (What are implicits)

As time goes by and you get used to working with Scala you eventually come across a similar compiler error message:

随着时间的流逝,您习惯了使用Scala,最终会遇到类似的编译器错误消息:

error: could not find implicit value for parameter reads: JsonReads[Order]

错误 :找不到参数读取的隐式值:JsonReads [Order]

I like checking the meaning of programming jargon in the English dictionary to really try and understand why something is called the way it is. So let’s double check what implicit means in the language of humans. Collins dictionary says:

我喜欢检查英语词典中编程术语的含义,以真正尝试并理解为什么这样称呼它。 因此,让我们仔细检查一下隐含对人类语言的含义。 柯林斯词典说:

“Something that is implicit is expressed in an indirect way.”

隐式的事物以间接方式表达。”

So we can adapt it to match the Scala realm:

因此,我们可以对其进行调整以匹配Scala领域:

Something being implicit means it is passed and used in an indirect way, “behind the scenes”.

隐含的东西表示它是在“幕后”以间接方式传递和使用的。

Here is an example of an implicit value usage. (Disclaimer: this is a horrible practise but it illustrates the idea behind implicits in just a few lines of code. Please don’t ever use implicits for something like this). Pay close attention to package location of the code snippets:

这是一个隐式值用法的示例。 (免责声明:这是一种可怕的做法,但它仅用几行代码就说明了隐式背后的想法。请不要对此类内容使用隐式)。 请密切注意代码段的打包 位置

// inside com.taxwebsite.StaticValues...
implicit val vat: Double = 0.19
...// inside com.taxwebsite.TaxCalulator
...
def getPriceWithTax(amount: Int)(implicit vat: Double) = amount + amount * vat
...// inside com.taxwebsite.PriceServiceimport com.taxwebsite.StaticValues.vat <- important chunk here!
...
def getFinalPrice(amount: Int) = getPriceWithTax(amount)
...

In the above code example we have an implicit value vat that gets imported into the scope of PriceService. Because the vat value is marked as implicit it’s then not necessary to explicitly say getPriceWithTax(amount)(vat) to satisfy getPriceWithTax signature — vat is passed “behind the scenes”.

在上面的代码示例中,我们有一个隐式值vat ,将其导入PriceService的范围。 由于vat值被标记为隐式,因此不必明确说出getPriceWithTax(amount) (vat)来满足getPriceWithTax签名- vat是“ 在幕后 ”传递的。

A few ways how we could reproduce the error I mentioned right in the beginning would be:

我们可以重现我在一开始就提到的错误的几种方法是:

  • If we removed the import from PriceService

    如果我们从PriceService删除了导入

  • …or if we didn’t mark val vat as implicit

    …或者如果我们不将val vat标记为隐式

  • …or if val vat was not a Double

    …或者如果val vat不是Double

  • …or if getPriceWithTax was not a Double

    …或者如果getPriceWithTax不是Double

Important note!Hope the above ways to break such trivial piece of code illustrate the amount of moving parts and what kind of headaches you could give your future colleagues if you go for a similar implementation of something simple and you unnecessarily add implicits.

重要的提示! 希望以上打破此类琐碎代码的方法能够说明不断变化的部分,以及如果您对简单的事物进行类似的实现并且不必要添加隐式内容,那么可能给未来的同事带来什么麻烦。

编译器如何知道哪个隐式对象“注入”到哪里? (How does the compiler know which implicit to “inject” where?)

You can’t spell “earth” without “art” but you can definitely spell “compiler” without “magic”.

没有“艺术”就不能拼写“地球”,但是没有“ 魔术 ”就可以绝对拼写“ 编译器 ”。

Implicit resolution

隐式解析

Let’s talk about where the compiler looks for implicits and also what it looks at. Let’s base on the above example and think like the compiler. (*)

让我们谈谈编译器在哪里寻找隐式内容,以及它的内容。 让我们基于上面的示例,像编译器一样思考。 (*)

  1. I am calling getFinalPrice

    我正在打电话给getFinalPrice

  2. I see that getFinalPrice calls getPriceWithTax that needs an implicit value of type Double (notice how compiler doesn’t care for the name of the value. Just its type)

    我看到getFinalPrice调用了getPriceWithTax ,它需要一个Double类型隐式值 (注意编译器不关心值的名称。只是它的类型 )

  3. Is there any implicit Double value inside of getFinalPrice? No.

    getFinalPrice内部是否有任何隐式Double值? 没有。

  4. Is there any implicit Double value in PriceService? No.

    PriceService是否有任何隐式Double值? 没有。

  5. Is there a companion object to PriceService? No.

    PriceService是否有配套对象? 没有。

  6. Is there an import in PriceService that contains an implicit Double value? Yes.

    PriceService中是否有包含隐式Double值的导入? 是。

  7. Ok, then I am assuming this value is the implicit Double value getPriceWithTax needs. I will pass it implicitly “behind the scenes”.

    好的,那么我假设此值为隐式DoublegetPriceWithTax所需。 我将在“幕后”隐式传递它。

什么可以隐式? (What can be implicit?)

It’s not just val or var. It can be implicit def, implicit class, implicit object too.

不只是valvar 。 它也可以是implicit defimplicit classimplicit object

为什么要使用隐式 (Why to use implicits)

I purposely skip all the details at the end of What can be implicit? section. I feel like in most of the materials online there is an overload of examples yet two most important takeaways are always missing that advanced beginners often struggle with. Here they are:

我特意跳过了什么可以隐含? 部分。 我觉得在线上的大多数材料中都有很多例子,但始终缺少高级入门者经常遇到的两个 最重要的要点 。 他们来了:

  1. Implicit objects (vals, classes, defs, objects) on their own have very little everyday programming usage. There is a very strict set of use cases where you would add them to your code. In real world, you don’t just write an implicit object and pass it around like all the materials online show (including my example with tax service above!)

    隐式对象(val,类,def,对象)本身很少有日常编程用途。 有一组非常严格的用例,您可以将它们添加到代码中。 在现实世界中,您不只是编写一个隐式对象并将其像所有在线显示的资料一样传递(包括上面带有税务服务的示例!)

  2. Implicits come into play in a much bigger picture, on a design-pattern abstraction level. Two examples to come into my mind are implementing type classes or implicit conversions. Let’s look at both now.

    设计模式抽象级别隐含的含义要大得多 。 我想到的两个例子是实现类型类隐式转换 让我们现在看看。

类型类别 (Type classes)

I will skip the implementation details here. It is a bit of a challenging design concept with implicits in its heart. I am not linking to any materials simply because type classes are too difficult to grasp just from reading about them and I don’t want for you to get discouraged. I will cover them one day in an article. Long story short a type class pattern allows you to (among other things) add a new method to an existing type without altering this type’s source code.

我将在此处跳过实施细节。 这是一个具有挑战性的设计概念, 其内在隐含隐含的含义。 我不会链接到任何材料,仅仅是因为类型类很难仅仅通过阅读它们就很难掌握,而且我不希望您灰心。 我将在一篇文章中介绍它们。 长话短说,类型类模式允许您(除其他外)在现有类型中添加新方法而无需更改该类型的源代码。

So using a type class you could extend typeString to be able to call something funky on it:

因此,使用类型类可以扩展String类型,以便能够在其上调用一些时髦的东西:

"Berlin".getCountry

This is known as ad-hoc polymorphism. Type classes are used extensively in some functional Scala libraries, most famous probably would be cats.

这被称为即席多态性 。 类型类在某些功能性Scala库中广泛使用,最著名的可能是cats

Fun fact: Type classes and extension methods are supposed to be much easier to use in the latest version of Scala, Scala 3. I admit I haven’t tried it yet.

有趣的事实:在最新版本的Scala Scala 3中,类型类和扩展方法应该更容易使用。我承认我还没有尝试过。

隐式转换 (Implicit conversion)

Another design-pattern level concept, often used to achieve magnet pattern. You can thing of it as a number of methods that the compiler tries to apply when an incorrect argument type is passed to an existing function or a class. They convert the type implicitly, “behind the scenes” to match the called function’s signature. An interesting example of implicit conversion would be how it’s possible to pass Scala Int to Java’s methods that expect Java’s Integer.

另一个设计模式级别的概念,通常用于实现磁体模式 。 您可以将其视为当不正确的参数类型传递给现有函数或类时编译器尝试应用的许多方法。 它们隐式转换类型 ,“在幕后”以匹配被调用函数的签名。 隐式转换的一个有趣例子是如何将Scala Int传递给期望Java的Integer的Java方法。

何时使用隐式 (When to use implicits)

So finally we get to the topic that probably most of the readers await.

所以最后我们进入了可能大多数读者都在等待的话题。

The Real Life.

现实生活。

I have a feeling that most of you at this stage understand the idea behind implicits yet cannot think of a single use case where you could use them.

我感觉到,在此阶段,大多数人都了解隐式背后的思想,却无法想到可以使用它们的单个用例。

And that’s good. That’s exactly it. As I already mentioned above you don’t just write an implicit object and pass it around like most of the materials that cover implicits would suggest.

那很好。 就是这样 。 正如我上面已经提到的,您不只是编写一个隐式对象并将其传递,就像涵盖隐式的大多数材料所建议的那样。

A new codebase polluted with incorrect usage of implicits is doomed to become a legacy codebase very quick.

被错误使用隐式代码污染的新代码库注定很快会变成旧式代码库。

Unless used within the frames of a standard well known design pattern or for a well known use case implicits have the power to make your code horribly unreadable and build up significant tech debt very quickly.

除非在标准的众所周知的设计模式的框架内使用,或在众所周知的用例中使用,否则隐式函数将使您的代码极其难以阅读并Swift建立起大量技术债务。

In real life you come across implicits when:

在现实生活中,您会在以下情况下遇到隐式:

您需要满足图书馆的依赖性 (You need to satisfy a library dependency)

There is a ton of libraries out there that because of their inner design (often type classes) will require you to have some implicit value in scope. To name three examples:

有大量的库因为它们的内部设计(通常是类型类)而需要您在范围上具有一些隐式值。 举三个例子:

  • To use most of the JSON serialiser libraries with custom types you will need to create an implicit read/write object that will expose an extension method like .toJson on your custom type and then import it. Then you can call: Person("Andrew").toJson

    要将大多数JSON序列化程序库与自定义类型一起使用,您将需要创建一个隐式读/写对象 ,该对象将在自定义类型上公开.toJson这样的扩展方法,然后将其导入。 然后,您可以调用: Person("Andrew").toJson

  • To use Scala Future you will need an implicit execution context.

    要使用Scala Future,您将需要一个隐式执行上下文

  • Scala’s Duration looks type class-y, doesn’t it?

    Scala的Duration看起来是 -y,不是吗?

// extension methods for Int imported here, you will find a lot of implicits in this package
import scala.concurrent.Duration._1.second // "second" is an extension method on Int
10.millis // ...so is "millis"

您使用分布式跟踪 (You work with distributed tracing)

I have seen an interesting usage of implicits and that is to implicitly pass a distributed tracing ID through the system together with each request. As the outcome the business logics were not polluted with distributed tracing ID references — after all they have nothing to do with the business logics.

我已经看到了一个有趣的隐式用法,即隐式地将分布式跟踪ID与每个请求一起传递给系统。 结果,业务逻辑未受到分布式跟踪ID引用的污染-毕竟它们与业务逻辑无关。

您实现类型类模式或隐式转换 (You implement a type class pattern or implicit conversion)

It’s totally ok if you don’t know how to do it or unsure when to use it. At work so far I haven’t had to implement any of these two from scratch myself. In most of the cases you won’t be writing them yourself and will likely be using something that already exists in a library.

如果您不知道怎么做或不确定何时使用它,那完全可以。 到目前为止,我还不需要自己从头开始实现这两个方面。 在大多数情况下,您将不会自己编写它们,而可能会使用库中已经存在的东西。

Image for post
Unsplash Unsplash

结论 (Conclusions)

Let’s summarise.

让我们总结一下。

What are implicits?Objects that are marked with a keyword “implicit”. That can be vals, vars, objects, classes, defs. If listed as a dependency of another object they are picked up by the compiler “behind the scenes”. Compiler only looks at types of the implicit values, not names.

什么是隐式? 用关键字“隐式”标记的对象。 可以是val,var,对象,类,defs。 如果列出为另一个对象的依赖项,则它们将由编译器“在幕后”拾取。 编译器仅查看隐式值的类型,而不查看名称。

Where are they used?Mainly in functional libraries or in well known design patterns like type class or implicit conversions.

它们在哪里使用? 主要在函数库中或在众所周知的设计模式中,例如类型类隐式转换

When should I use them in my code?Avoid at all cost unless for you are implementing a well known design pattern that other Scala engineers can understand or satisfying a library dependency. If used incorrectly implicits will make the codebase difficult to read, test and maintain. Long story short: If something else needs an implicit — totally fine, provide it. If you think something in the code you are writing needs to be made implicit: 99 out of 100 cases — it doesn’t.

我应该何时在代码中使用它们? 除非您正实施其他Scala工程师可以理解满足库依赖性的众所周知的设计模式,否则不惜一切代价避免。 如果使用不当,则隐式将使代码库难以阅读,测试和维护。 长话短说:如果其他内容需要隐式-完全可以,请提供它。 如果您认为所编写的代码中的某些内容需要隐式表示:100个案例中有99个(不是)。

(*) of course there is so much more to it — for the sake of simplicity I do skip most of the details in this explanation.

(*)当然还有更多的内容-为简单起见,我确实跳过了此解释中的大多数细节。

翻译自: https://levelup.gitconnected.com/scala-journals-the-mystery-of-implicits-debunked-a38def8959b

scala 隐式转换

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值