Search code examples
scalatypesimplicitimplicit-typing

Is it possible to define a function return type based on a defined mapping from the type of a function argument?


Ideally I'd like to be able to do the following in Scala:

import Builders._

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

In principle the goal is simply to be able to 'map' TypeToBuild to TypeToBuildBuilder using external mapping definitions (i.e. assume no ability to change these classes) and leverage this in type inferencing.

I got the following working with AnyRef types:

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]
}

Note that the type is passed as a function argument rather than a type argument.

I'd be supremely happy just to find out how to get the above working with Any types, rather than just AnyRef types. It seems this limitation comes since Singleton types are only supported for AnyRefs (i.e. my use of TypeToBuild.type).

That being said, an answer that solves the original 'ideal' scenario (using a type argument instead of a function argument) would be fantastic!

EDIT

A possible solution that requires classOf[_] (would really love not needing to use 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]
}

Being able to just use builder(TypeToBuild) is really just a win in elegance/brevity. Being able to use builder[TypeToBuild] would be cool as perhaps this could one day work (with type inference advancements in Scala):

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

Here is a complete, working example using classOf: http://ideone.com/94rat3


Solution

  • I'll resort to answering my own question since a Redditor ended up giving me the answer I was looking for and they appear to have chosen not to respond here.

    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")
    }
    

    Then you will get:

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

    You can see the reddit post here: http://www.reddit.com/r/scala/comments/2542x8/is_it_possible_to_define_a_function_return_type/

    And a full working version here: http://ideone.com/oPI7Az