Question

I have two case classes

case class StringCaseClass(argument: String)

case class IntCaseClass(argument: Int)

I want to define a structural type which will match the companion object of both of these

type HasApply1 {
  def apply[A, R](argument: A): R
}

This will compile fine, but when I try to use it like this

def method(caseClass: HasApply1) {
  // whatever
}

method(StringCaseClass)

I will get a compiler error

found   : StringCaseClass.type
required: WithApply1
            (which expands to)  AnyRef{def apply[A, R](string: A): R}

Is there any way of accomplishing this? If I redefine the structural type to have concrete types for A and R it will compile correctly, but then I lose the flexiblity

Was it helpful?

Solution

@aloiscochard's comment is almost there. What he forgot to mention is that case class companion objects already implement the appropriate FunctionN trait, so you can simply do this,

scala> case class StringCaseClass(argument: String)
defined class StringCaseClass

scala> case class IntCaseClass(argument: Int)
defined class IntCaseClass

scala> def method[A, R](caseClass: A => R, a: A) = caseClass(a)
method: [A, R](caseClass: A => R, a: A)R

scala> method(StringCaseClass, "foo")
res0: StringCaseClass = StringCaseClass(foo)

scala> method(IntCaseClass, 23)
res1: IntCaseClass = IntCaseClass(23)

OTHER TIPS

In general you should avoid structural typing as it's very expensive. The call will be converted into a reflection call because of limitations in the JVM. When you start using scala 2.10 structural types will result in a warning at compile time (though you could disable that using a flag).

If you're looking into a more general way to add functionality to classes that don't share an inheritance hierarchy you could use Type Classes.

Here's a quick example:

trait CanCreateRFromA[A,R]{
    def createNew(a:A): R
}

implicit object CanCreateBlahFromInt extends CanCreateRFromA[Int,Blah2]{
    def createNew(i:Int):Blah2 = new Blah2(i)
}


implicit object CanCreateBlah1FromString extends CanCreateRFromA[String,Blah1]{
    def createNew(s:String):Blah1 = new Blah1(s)
}

case class Blah1(something:String)
case class Blah2(something:Int)

def createRFromA[A,R](a:A)(implicit tc:CanCreateRFromA[A,R])= tc.createNew(a)

Then you can call:

createRFromA(1) // This gives a Blah2
createRFromA("1") // This gives a Blah1

Again I'm not sure what you're trying to accomplish, but it probably is possible to do what you want with a type class and it will be much faster.

You didn't pass an instance of StringCaseClass to your method. What you passed there is companion object of StringCaseClass (which is automatically generated for case classes).

Try if this works: method(StringCaseClass("dummy")).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top