当前位置: 首页 > 知识库问答 >
问题:

蕴涵有什么用途,如何在Scala中使用它们?[关闭]

陈实
2023-03-14

对于来自Java和其他语言(如CC++)的程序员来说,implicit关键字是一个非常模糊的东西,因此了解Scala中的implicit关键字非常重要。在Scala中如何使用implicit

大多数情况下,问题是“在Scala中蕴涵的用法是什么?”是在“如何编写/使用隐式转换?”、“如何使用隐式类型类?”的意义上回答的。等等。

对于新的Scala程序员(至少是我认识的那些人)来说,这样的回答大多数时候给人的印象是implicits实际上只是一个“美化”工具,

val s = (new RichInt(5)).toString + ":: Well"

只是为了

val s = 5 + ":: Well"

一些持“裸机”哲学的程序员甚至倾向于不喜欢隐藏这些细节的implicit的存在。

不知何故,重要的问题是“在Scala中的”用途和重要性“是什么?”不知怎么的被忽视了也没人回答。

特别是了解Scala的程序员,他们在一定程度上理解“如何使用implicits”,并且有时会为这个问题的“隐藏魔法”所困扰--“implicits有什么特别之处,以至于Scala创建者甚至选择使用implicits?”

我不是很确定OP在作为Scala程序员探索implicits时是否也考虑了这些问题。

令人惊讶的是,我在任何地方都找不到这些问题的答案。一个原因是,即使是回答implicits的实际“需要/好处”的一个用例,也需要大量的解释。

我认为这些问题值得社区关注,以帮助新的Scala程序员。我试图简单地解释implicits的一个用例。我希望看到更多的人(更有见识的人)有兴趣回答这个问题。


共有1个答案

陆翰学
2023-03-14

我更多的是从“implicits有什么用途(为什么在某些情况下需要implicit)”的角度来回答这个问题,而不是从“在某些情况下如何使用implicit”的角度来回答这个问题(这个问题的答案可以在google上搜索)。

在进行解释时,我在某些地方以可互换的方式使用了typeclass。它仍然应该以一种简单的方式传达预期的信息,但它可能表明typeclass是相似的错误概念。我看不出有什么办法可以不对答案进行重大修改就能解决这个问题。请记住,typeclass是不同的东西。

implicit的实际功能。

它其实很简单,implicit做的正是顾名思义。如果需要查找所述类型的“Go to”实例,它将事物标记为相应作用域中的“Go to”实例/值。

因此,只要需要a类型的“Go To”实例,我们就可以说a类型的隐式实例/值就是a类型的“Go To”实例。

要将任何实例/值标记为相应范围内可用的implicity(“转到”),我们需要使用implicit关键字。

scala> implicit val i: Int = 5
// i: Int = 5

我们如何在需要时调用隐式实例/值?

调用implicit的最直接方法是使用implictly方法。

val gotoInt = implicitly[Int]
// gotoInt: Int = 5

或者,我们可以使用implicit参数定义方法,以期望implicit实例在它们正在使用的范围中的可用性,

def addWithImplictInt(i: Int)(implicit j: Int): Int = i + j

请记住,我们也可以在不指定implicit参数的情况下定义相同的方法,

def addWithImplicitInt(i: Int): Int = {
  val implictInt = implicitly[Int]
  i + implictInt
}

请注意,带有implicit参数的首选项向用户表明,method需要隐式参数。由于这个原因,大多数情况下应该选择implicit参数(异常始终存在)。

为什么我们实际使用implicit

这与我们可以使用implicit值的可能方式有点不同。我们正在讨论为什么我们“实际上”需要使用它们。

答案是帮助编译器确定type,并帮助我们编写类型安全的代码,以解决问题,否则将导致运行时类型比较,最终我们将失去编译器所能提供的所有帮助。

请考虑以下示例,

假设我们正在使用一个定义了以下类型的库,

trait LibTrait

case class LibClass1(s: String) extends LibTrait
case class LibClass2(s: String) extends LibTrait
case class LibClass3(s: String) extends LibTrait
case class LibClass4(s: String) extends LibTrait
case class YourClass1(s: String) extends LibTrait
case class YourClass2(s: String) extends LibTrait

case class OthersClass1(s: String) extends LibTrait
case class OthersClass2(s: String) extends LibTrait
// impl_1
def performMySpecialBehaviour[A <: LibTrait](a): Unit

但是,上面的内容将允许扩展libtrait的所有内容。

一种选择是为所有“支持的”类定义方法。但是由于您不控制libtrait的扩展,所以您甚至不能这样做(这也不是一个很好的选择)。

另一个选择是为您的方法建模这些“限制”,

trait MyRestriction[A <: LibTrait] {
  def myRestrictedBehaviour(a: A): Unit
}
// impl_2 
def performMySpecialBehaviour(mr: MyRestriction): Unit

因此,现在用户首先必须将他们的实例转换为MyRestriction的一些实现(这将确保满足您的限制)。

但是查看PerformMySpecialBehavior的签名,您将看不到与实际需要的任何相似之处。

此外,您的限制似乎绑定到class而不是实例本身,因此我们可以使用typeclass

// impl_3
def performMySpecialBehaviour[A <: LibTrait](a: A, mr: MyRestriction[A]): Unit

用户可以为其定义type-class实例并将其与方法一起使用

object myRestrictionForYourClass1 extends MyRestriction[YourClass1] {
  def myRestrictedBehaviour(a: A): Unit = ???
}

但是查看PerformMySpecialBehavior的签名,您将看不到与实际需要的任何相似之处。

但是,如果您要使用考虑implicits的使用,我们可以更清楚地了解它的用法

// impl_4
def performMySpecialBehaviour[A :< LibTrait](a: A)(implicit ev: MyRestriction[A]): Unit

请记住,libtrait仍然开放进行扩展。让我们考虑一下你或者你的团队中的某个人最后跟了一个,

trait YoutTrait extends LibTrait

case class YourTraitClass1(s: String) extends YoutTrait
case class YourTraitClass2(s: String) extends YoutTrait
case class YourTraitClass3(s: String) extends YoutTrait
case class YourTraitClass4(s: String) extends YoutTrait

注意,YoutTrait也是一个开放的特性。

因此,它们都有自己的myrestriction的相应实例,

object myRestrictionForYourTraitClass1 extends MyRestriction[YourTraitClass1] {...}
object myRestrictionForYourTraitClass2 extends MyRestriction[YourTraitClass1] {...}
...
...
def otherMethod[A <: YoutTrait](a: A): Unit = {
  // do something before
  val mr: MyRestriction[A] = ??????????
  performMySpecialBehaviour(a, mr)
  // do something after
}
def otherMethod[A <: YoutTrait](a: A)(implicit ev: MyRestriction[A]): Unit = {
  // do something before
  performMySpecialBehaviour(a)
  // do something after
}

并且只要YoutTrait的所有子类型的MyRestriction实例都在作用域内,就可以工作。否则代码将无法编译。

因此,如果有人添加了YoutTrait的新子类型YourTraitClassXX并且忘记了确保定义了MyRestriction[YourTraitClassXX]实例并使其在正在进行任何OtherMethod调用的范围中可用,则代码将无法编译。

这只是一个示例,但它应该足以说明implicits在Scala中的实际用途是“为什么”和“什么

关于implicits还有很多话要说,但这会使答案太长;它已经是了。

本例中的用例对参数类型a设置了一个绑定,以使type ClassTypeClass[a]的实例在作用域中。它被称为Context Bound,

def restricted[A, B](a: A)(implicit ev: TypeClass[A]): B 

或者,

def restricted[A: TypeClass, B](a: A): B
 

注::如果您注意到答案有任何问题,请评论。欢迎任何反馈。

 类似资料:
  • 问题内容: Node.js module.exports的用途是什么,如何使用它? 我似乎找不到任何相关信息,但是正如我在源代码中经常看到的那样,它似乎是Node.js的重要组成部分。 根据Node.js文档: 模组 对当前的引用 。特别 是与导出对象相同。请参阅 以获取更多信息。 但这并没有真正的帮助。 究竟是做什么的,一个简单的例子是什么? 问题答案: 是调用结果实际返回的对象。 该变量最初设

  • 问题内容: 如何在hibernate中使用级联和逆运算?定义它们的过程/标签是什么?它们彼此相关吗?它们如何有用? 问题答案: 在通过中介表进行多对多关系的情况下;“级联”表示是否在子表中创建/更新记录。而“反向”表示是否在中间表中创建/更新记录 例如,假设情况1下的学生可以拥有多部电话。因此,学生班级拥有“手机套”的属性。另外,一部电话可以由多个学生拥有。因此,“电话”类具有“学生组”的属性。s

  • 所以我知道MobileAds。initialize()用于减少会话第一个ad请求的延迟。但我看到的结构是MobileAds。初始化(context,app_id),那么“initializationStatus”有什么用呢-

  • 对于PL/pgSQL来说是全新的,这个函数中的双美元符号是什么意思: 我猜,在中,是占位符。 最后一行有点玄机:< code > $ $ LANGUAGE plpgsql STRICT IMMUTABLE; 顺便问一下,最后一行是什么意思?

  • 问题内容: 如何在休眠中使用级联和逆运算?定义它们的过程/标签是什么?它们彼此相关吗?它们有什么用? 问题答案: 在通过中介表进行多对多关系的情况下;“级联”表示是否在子表中创建/更新记录。而“反向”表示是否在中间表中创建/更新记录 例如,假设在方案1下,一个学生可以拥有多部电话。因此,学生班级拥有“手机套”属性。另外,一部电话可以由多个学生拥有。因此,“电话”类具有“学生组”的属性。stud_p

  • iOS 6和Xcode 4.5有一项新功能,称为“Unwind Segue”: 展开分段可以允许转换到故事板中的现有场景实例 除了Xcode 4.5发行说明中的这一简短条目外,UIViewController现在似乎有两个新方法: 展开段如何工作以及它们可以用于什么?