是否可以根据函数参数类型定义的映射来定义函数返回类型?

时间:2014-05-07 11:51:05

标签: scala types implicit implicit-typing

理想情况下,我希望能够在Scala中执行以下操作:

import Builders._

val myBuilder = builder[TypeToBuild] // Returns instance of TypeToBuildBuilder
val obj = myBuilder.methodOnTypeToBuildBuilder(...).build()

原则上,目标只是能够使用外部映射定义(即假设无法更改这些类)和“杠杆”将TypeToBuild“映射”到TypeToBuildBuilder这在类型推理中。

我使用AnyRef类型进行了以下操作:

import Builders._

val myBuilder = builder(TypeToBuild)
myBuilder.methodOnTypeToBuildBuilder(...).build()

object Builders {
    implicit val typeToBuildBuilderFactory =
        new BuilderFactory[TypeToBuild.type, TypeToBuildBuilder]
    def builder[T, B](typ: T)(implicit ev: BuilderFactory[T, B]): B = ev.create
}

class BuilderFactory[T, B: ClassTag] {
    def create: B = classTag[B].runtimeClass.newInstance().asInstanceOf[B]
}

请注意,类型作为函数参数而不是类型参数传递。

如果您想了解如何使用Any类型而不仅仅是AnyRef类型,我会非常高兴。似乎这种限制是因为Singleton类型仅支持AnyRef s(即我使用TypeToBuild.type)。

话虽如此,一个解决原始“理想”场景的答案(使用类型参数而不是函数参数)将是太棒了!

修改

需要classOf[_]的可能解决方案(真的不需要使用classOf!):

import Builders._

val myBuilder = builder(classOf[TypeToBuild])
myBuilder.methodOnTypeToBuildBuilder(...).build()

object Builders {
    implicit val typeToBuildBuilderFactory =
        new BuilderFactory[classOf[TypeToBuild], TypeToBuildBuilder]
    def builder[T, B](typ: T)(implicit ev: BuilderFactory[T, B]): B = ev.create
}

class BuilderFactory[T, B: ClassTag] {
    def create: B = classTag[B].runtimeClass.newInstance().asInstanceOf[B]
}

能够使用builder(TypeToBuild)真的只是优雅/简洁的胜利。能够使用builder[TypeToBuild]会很酷,因为这可能有一天可以工作(在Scala中使用类型推断进展):

val obj: TypeToBuild = builder.methodOnTypeToBuildBuilder(...).build();

以下是使用classOfhttp://ideone.com/94rat3

的完整实用示例

3 个答案:

答案 0 :(得分:2)

是的,Scala支持基于参数类型的返回类型。这方面的一个示例是集合API中的方法,如map,它使用CanBuildFrom类型类来返回所需的类型。

我不确定你要对你的示例代码做什么,但也许你想要这样的东西:

trait Builder[-A, +B] {
    def create(x: A): B
}

object Builders {
    implicit val int2StringBuilder = new Builder[Int, String] {
      def create(x: Int) = "a" * x
    }
    def buildFrom[A, B](x: A)(implicit ev: Builder[A, B]): B = ev.create(x)
}

import Builders._

buildFrom(5)

newInstance的魔力仅适用于具有不带参数的构造函数的具体类,因此它可能不够通用,无法使用。

答案 1 :(得分:1)

如果你不害怕隐含的转换,你可以这样做:

import scala.language.implicitConversions

trait BuilderMapping[TypeToBuild, BuilderType] {
    def create: BuilderType
}

case class BuilderSpec[TypeToBuild]()

def builder[TypeToBuild] = BuilderSpec[TypeToBuild]

implicit def builderSpecToBuilder[TypeToBuild, BuilderType]
    (spec: BuilderSpec[TypeToBuild])
    (implicit ev: BuilderMapping[TypeToBuild, BuilderType]) = ev.create




case class Foo(count: Int)

case class FooBuilder() {
    def translate(f: Foo) = "a" * f.count
}

implicit val FooToFooBuilder = new BuilderMapping[Foo, FooBuilder] {
  def create = FooBuilder()
}

val b = builder[Foo]
println(b.translate(Foo(3)))

隐式转换并不太糟糕,因为它们被限制在这些面向构建器的类型中。需要进行转换才能使b.translate有效。

看起来像wingedsubmariner的答案是你想要的大部分,但你不想指定TypeToBuildBuilderType(你没有必要想传递一个值)。为实现这一目标,我们需要将单个通用签名分解为两个部分,这就是BuilderSpec类型存在的原因。

也可以使用部分通用应用程序之类的东西(参见a question that I asked earlier的答案),尽管我现在无法将这些内容放在我的脑海中。

答案 2 :(得分:0)

我会回答我自己的问题,因为Redditor最终给了我正在寻找的答案,他们似乎选择不回复。

trait Buildable[T] {
  type Result
  def newBuilder: Result
}

object Buildable {
  implicit object ABuildable extends Buildable[A] {
    type Result = ABuilder
    override def newBuilder = new ABuilder
  }
  implicit object BBuildable extends Buildable[B] {
    type Result = BBuilder
    override def newBuilder = new BBuilder
  }
}

def builder[T](implicit B: Buildable[T]): B.Result = B.newBuilder

class ABuilder {
  def method1() = println("Call from ABuilder")
}

class BBuilder {
  def method2() = println("Call from BBuilder")
}

然后你会得到:

scala> builder[A].method1()
Call from ABuilder

scala> builder[B].method2()
Call from BBuilder

您可以在此处查看reddit帖子:http://www.reddit.com/r/scala/comments/2542x8/is_it_possible_to_define_a_function_return_type/

这里有一个完整的工作版本:http://ideone.com/oPI7Az