3

在 Scala 中,我如何将隐式转换的导入委托给我的范围,这样我就不必拥有一个大的“环境”类,它既提供库函数/值(对于我正在创建的 DSL)也提供隐式转换?

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

导入 MyDslEnvironment._

?

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

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

4

2 回答 2

3

这可以使用特征轻松实现。只需根据需要在尽可能多的特征中定义隐式转换(以实现模块化),并将特征混合在单个对象(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)

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

于 2012-10-15T20:55:03.130 回答
3

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

假设您的工作将在包 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 中,仅仅为了实用功能而必须使类充满静态成员似乎总是很狡猾。包对象为这些实用程序提供了非常优雅的命名空间,包括隐式转换。

于 2012-10-16T17:03:00.093 回答