【问题标题】:Is it possible to define a function return type based on a defined mapping from the type of a function argument?是否可以根据函数参数类型的定义映射来定义函数返回类型?
【发布时间】:2014-05-07 11:51:04
【问题描述】:

理想情况下,我希望能够在 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 类型仅支持 AnyRefs(即我使用 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();

这是一个使用 classOf 的完整工作示例:http://ideone.com/94rat3

【问题讨论】:

    标签: scala types implicit implicit-typing


    【解决方案1】:

    是的,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 的魔力只适用于具有不带参数的构造函数的具体类,因此它可能不够通用而没有用处。

    【讨论】:

    • 感谢您的意见!您给出的解决方案有一个固定的Builder[B] 返回类型,所以我只能在返回值上使用所有Builder[_] 通用的方法。此外,它还需要修改我正在尝试使用的对象(我指定了“假设无法更改这些类”)。我理解 newInstance 限制并对此感到满意。我要做的是根据给定的类型参数返回一个完全不同的类,然后直接调用该返回类型独有的方法。谢谢你告诉我CanBuildFrom - 我会调查的。
    • 好的,所以CanBuildFrom 技术与我在解决方案中所做的基本相似。但是,它用于可以根据调用方法的实例和给定参数推断类型的情况。我只是想根据他们构建的对象来查找构建器,所以除非我将 classOf[ObjectToBuild] 作为参数传递,否则我没有这个。这实际上是我的整个问题的一个可能的解决方案 - 但是找到一种仅使用类型而不使用 classOf 的方法会很棒。
    • CanBuildFrom 模式也允许从返回类型推断类型。例如,阅读scala.collection.breakOutstackoverflow.com/questions/1715681/scala-2-8-breakout。基本上,list.map(x => x)(collection.breakOut) 会根据上下文返回不同的类型。
    • 确实它支持基于预期返回类型的推断,就像 Scala 一样。我正在寻找基于给定类型参数的方法指定返回类型,并结合一些将该类型映射到另一种类型的定义。因此,如果我以某种方式定义 ClassA 映射到 ClassB,那么 if 我指定 myMethod[ClassA] 它的返回类型是 ClassB。通常,给定已知的映射 X -> Y,myMethod[X] 的返回类型为 Y。
    • 您的意思是X 既没有出现在参数中,也没有出现在返回类型中?没有任何合理的理由这样做。您要么想传递 Y 作为类型参数,要么显式传递构建器实例。
    【解决方案2】:

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

    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 的答案),尽管我现在无法将这些部分放在一起。

    【讨论】:

      【解决方案3】:

      我将求助于回答我自己的问题,因为 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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-03-18
        • 2022-09-23
        • 1970-01-01
        • 1970-01-01
        • 2020-08-24
        • 2020-07-24
        • 1970-01-01
        相关资源
        最近更新 更多