Scala委托隐式转换的导入

3
在Scala中,我如何委托将隐式转换导入到我的作用域中,以便我不必拥有一个提供库函数/值(为我创建的DSL)以及隐式转换的大型“环境”类?
换言之,我能否将我的隐式转换从对象移动,并在编写以下代码时仍然导入它:
``` import MyDslEnvironment._ ```
这样做的目的是使我的框架的导入和使用简单轻便,只需要一个单独的导入语句即可为用户提供所需的DSL/框架功能。
PS:在任何人抨击我之前 - 是的,我意识到隐式转换可能存在的问题和不良影响。

你觉得把它们放在一个包对象里面行不行? - Steve Waldman
我向您推荐Josh Suereth的这个视频:http://vimeo.com/20308847 - pedrofurla
Steve> 请详细说明一下。我还没有涉足包对象 :) - Felix
@Felix 包对象对应于包,就像伴生对象对应于它们的伴生类或特质一样。每当您导入一个包时,如果定义了一个与其关联的包对象,则可以使用该包本身和相关的包对象中的所有内容。 - Ptharien's Flame
这很好。这可能是更好的抽象,因为对于用户来说,它会感觉像是导入一个框架而不是单例对象的内容。 - Felix
@Steve,你可以随意提出你的建议(并可能附上代码示例)。 我认为你用最好的解决方案解决了这个问题。 - Felix
2个回答

3

这可以通过使用特质(trait)轻松实现。只需在需要的特质中定义隐式转换,以实现模块化,并将这些特质混合在一个对象(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/

嗯,那很简单 :) 我会看看其他人有什么想法,否则我会接受这个。 - Felix
1
另外,请注意您可以很好地将MyDslEnvironment转换为您的主要公共包的包对象,以方便使用。只需将object MyDslEnvironment extends ...替换为package object mypackage extends ... - Régis Jean-Gilles

3

我的倾向是将隐式参数放在一个package object中。

假设你的工作将被定义在包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中,必须创建充满静态成员的类来存放实用函数,这似乎有些做作。而包对象则作为非常优雅的命名空间,包含了这些实用工具,包括隐式转换。

为了保险起见,假设我想创建一个包含测试的软件包。如果我创建一个包对象并从其他包对象(例如您上面展示的那个)导入dsl环境,它会在我的软件包中全局可用吗?还是我仍然需要在每个需要它的文件中导入它? - Felix
我认为你所建议的不会起作用 - 我的直觉是由Java框架制定的,也许这是错误的,但是从Java背景出发,我从来没有想过导入会影响除当前文件之外的任何东西(或者在Scala中更好的方式是框架导入语句的范围)。然而,如果你想要在多个包对象中使用导入,你可以尝试Régis Jean-Gilles的技巧:让包对象从具有隐式定义的特质继承。"包对象甚至可以继承Scala类和特质。" http://www.scala-lang.org/docu/files/packageobjects/packageobjects.html - Steve Waldman
你似乎是正确的。无论如何,最好还是这样做!也许有人会争辩说将所有测试放在同一个文件中,因为你可以在单个文件中组合多个对象,这样也不会太糟糕。 - Felix

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接