【问题标题】:Scala delegate import of implicit conversionsScala 委托导入隐式转换
【发布时间】:2012-10-15 18:14:04
【问题描述】:

在 Scala 中,我如何将隐式转换的导入委托给我的作用域,这样我就不必有一个大的“环境”类来提供库函数/值(对于我正在创建的 DSL)作为以及隐式转换?

简而言之,我可以将我的隐式转换从一个对象中移出,并在我编写时仍然将其导入:

导入 MyDslEnvironment._

?

这样做的目的是让我的框架的导入和使用变得简单而轻量,因为只有一个导入语句为用户提供了我的 DSL/框架所需的功能。

PS:在任何人激怒我之前 - 是的,我知道隐式转换可能带来的陷阱和肮脏。

【问题讨论】:

  • 将它们放在一个包对象中对你有用吗?
  • 我指给你看 Josh Suereth 的这个视频:vimeo.com/20308847
  • 史蒂夫> 请详细说明。我还没有真正涉足包对象:)
  • @Felix 包对象之于包,就像伴生对象之于它们的伴生类或特征一样。每当您导入一个包时,如果定义了一个包对象,则包本身和关联的包对象中的所有内容都是可用的。
  • 这很好。可能是更好的抽象,因为对于用户来说,感觉就像他/她正在导入框架而不是单例对象的内容。

标签: scala object import delegates implicit-conversion


【解决方案1】:

我倾向于将隐式放在一个包对象中。

假设您的工作将在包 com.acme.mydsl 中定义。您的源文件排列在目录层次结构 com > acme > mydsl 中。在目录 mydsl 中,定义一个像这样的对象:

package com.acme; //we are in the mydsl dir, but note no mydsl in
                  //in the package declaration

package object mydsl {

   implicit def idioticallyStringsAre5( s : String ) : Int = 5

   //define other utilities that should be available within
   //the package or importable

}

现在,你可以这样做了:

scala> import com.acme.mydsl._
import com.acme.mydsl._

scala> def sum( a : Int, b : Int ) = a + b
sum: (a: Int, b: Int)Int

scala> sum("hello", "there")
res0: Int = 10

通过导入 import com.acme.mydsl._,您可以获得所有包级别的函数定义,包括隐式转换。

我真的很喜欢包对象。在 Java 中,为了实用功能而必须使类充满静态成员似乎总是很不礼貌。包对象为这些实用程序提供了非常优雅的命名空间,包括隐式转换。

【讨论】:

  • 只是为了更好的衡量标准。假设我想创建一个包含测试的包。如果我创建一个包对象并从 OTHER 包对象(例如上面显示的那个)导入 dsl 环境,它会在我的包中全局可用,还是我仍然需要在每个需要它的文件中导入它?
  • 我认为你的建议是行不通的——我的直觉是由 Java 构建的,也许这是错误的,但从 Java 背景来看,我从不期望导入会影响除当前文件之外的任何内容(或者,在 Scala 中更好的是,构成 import 语句的范围)。但是,如果您希望导入在多个包对象中可用,您可以尝试 Régis Jean-Gilles 技巧:让包对象从带有隐式定义的特征继承。 “包对象甚至可以继承 Scala 类和特征。” scala-lang.org/docu/files/packageobjects/packageobjects.html
  • 你似乎是对的。无论如何,它肯定是最好的!有人可能会争论将所有测试放在同一个文件中,因为您可以将多个对象分组到一个文件中,这不会太糟糕。
【解决方案2】:

这可以通过使用特征轻松实现。只需根据需要在尽可能多的特征中定义隐式转换(以实现模块化),并将特征混合在单个对象(MyDslEnvironment)中。举例:

case class Foo( value: Int )
case class Bar( value: String )
object Baz {
  def test( foo: Foo, bar: Bar ) { println( foo + "," + bar ) }
}

trait ImplicitEnv1 {
  implicit def toFoo( value: Int ) = Foo( value )
}

trait ImplicitEnv2 {
  implicit def toBar( value: String ) = Bar( value )
}

object MyDslEnvironment extends ImplicitEnv1 with ImplicitEnv2

你可以这样做:

scala> import MyDslEnvironment._
import MyDslEnvironment._
scala> Baz.test( 123, "hello" )
Foo(123),Bar(hello)

实际上,您可以将所有代码(我上面的示例中的FooBarBaz)放入特征中,而不仅仅是隐式转换(这可能需要使用自类型注释)。至此,您将基本上实现了(臭名昭著的)蛋糕模式的变体之一。 见http://jonasboner.com/2008/10/06/real-world-scala-dependency-injection-di/

【讨论】:

  • 好吧。这很简单 :) 我会看看人们还有什么想法,否则我会接受。
  • 另外,请注意,为方便起见,您可以很好地将MyDslEnvironment 转换为主要公共包的包对象。只需将object MyDslEnvironment extends ... 替换为package object mypackage extends ...
猜你喜欢
  • 1970-01-01
  • 2019-08-12
  • 1970-01-01
  • 2016-05-29
  • 1970-01-01
  • 2012-04-15
  • 2018-09-16
  • 2011-09-12
  • 1970-01-01
相关资源
最近更新 更多