Как использовать структурный тип с универсальными параметрами?

У меня два кейса

case class StringCaseClass(argument: String)

case class IntCaseClass(argument: Int)

Я хочу определить структурный тип, который будет соответствовать сопутствующему объекту обоих этих

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

Это будет хорошо скомпилировано, но когда я попытаюсь использовать его вот так

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

method(StringCaseClass)

Я получу ошибку компилятора

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

Есть ли способ добиться этого? Если я переопределю структурный тип, чтобы иметь конкретные типы для A и R, он будет правильно компилироваться, но тогда я потеряю гибкость


person shinyhappydan    schedule 24.03.2013    source источник
comment
Для дальнейшего пояснения я хотел бы создать такой метод: def method[A](caseClass: HasApply1, a: A) = caseClass.apply(a)   -  person shinyhappydan    schedule 24.03.2013


Ответы (3)


Комментарий @aloiscochard почти готов. Он забыл упомянуть, что объекты-компаньоны класса case уже реализуют соответствующую черту FunctionN, так что вы можете просто сделать это,

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)
person Miles Sabin    schedule 24.03.2013
comment
Это именно то, что мне нужно. Я полагаю, что настоящий ответ на вопрос, который я задал, состоит в том, что вы не можете этого сделать ... но это работает для меня - person shinyhappydan; 25.03.2013
comment
Вау, Майлз, это великолепно. Вчера у меня не было возможности вернуться в SO, но это намного лучше, чем я мог бы придумать! +1! - person coltfred; 25.03.2013

В общем, вам следует избегать структурной типизации, поскольку это очень дорого. Вызов будет преобразован в вызов отражения из-за ограничений JVM. Когда вы начнете использовать scala 2.10 структурные типы, во время компиляции появится предупреждение (хотя вы можете отключить это с помощью флага).

Если вы ищете более общий способ добавления функциональности к классам, которые не разделяют иерархию наследования, вы можете использовать классы типов.

Вот небольшой пример:

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)

Тогда вы можете позвонить:

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

Опять же, я не уверен, чего вы пытаетесь достичь, но, вероятно, можно делать то, что вы хотите, с помощью класса типов, и это будет намного быстрее.

person coltfred    schedule 24.03.2013
comment
Облегченной альтернативой было бы использование типа Function1[A, R] (или A => R) вместо создания трейта CanCreateRFrom. - person Alois Cochard; 24.03.2013
comment
@coltfred см. мой комментарий к исходному сообщению для дальнейшего разъяснения того, что я пытаюсь сделать. Я не возражаю против расходов на операцию - person shinyhappydan; 24.03.2013

Вы не передали экземпляр StringCaseClass своему method. То, что вы там передали, является сопутствующим объектом StringCaseClass (который автоматически создается для классов case).

Попробуйте, если это сработает: method(StringCaseClass("dummy")).

person ghik    schedule 24.03.2013