Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Non-variable type argument

Tags:

scala

I've got the following models:

case class Questionnaire(List(Question[_])
case class Question[A](text: String, Answer[A])
case class Answer[A](value: A)

I'm trying to set all of the answers on a questionnaire depending on their type (answers can be of type String, Double or LocalDate):

val questionnaire = getMockQuestionnaireWithoutAnswers()
questionnaire.questions.map {
  case x: Question[String] => //...default the answer.value to some random string
  case x: Question[Double] => //...default the answer.value to some random double
  case x: Question[LocalDate] => //...default the answer.value to some random date
}

But I'm getting the error:

non-variable type argument String in type pattern examplenamespace.Question[String] is unchecked since it is eliminated by erasure.

I don't want to wrap all of the types into classes like this:

case class StringQuestion(question: Question[String])

What is the best way to avoid this error?

like image 507
Pierre Avatar asked Dec 19 '22 12:12

Pierre


2 Answers

Type erasure is eliminating the parametric type provided in the match expression, meaning that the difference between Question[String] and Question[Int] gets lost at runtime.

One way to circumvent this is to provide concrete implementations of that type, sothat the JVM cannot "erase" it.

I've used this construction in the past. There're probably other ways, but this one is simple enough:

case class Answer[A](a:A)
case class Question[A](text: String, answer: Answer[A])
case class Questionnaire(questions: List[Question[_]])

// concrete types
trait StringQuestion extends Question[String]
trait DoubleQuestion extends Question[Double]
trait IntQuestion extends Question[Int]

def resolve(questions: List[Question[_]]): List[String] = {
  questions.map {
    case x: StringQuestion => "Stringy"
    case x: DoubleQuestion => "Doubly"
    case x: IntQuestion => "Inty"
  }
}
like image 134
maasg Avatar answered Dec 21 '22 02:12

maasg


Try this approach:

questionnaire.questions.map {
  case Question(text, Answer(d:Double)) => //
  case Question(text, Answer(s:String)) => //
  case Question(text, Answer(ld:LocalDate)) => //
}

You have access to text of Question (text) and value of Answer (d, s or ld)

You also can use aliases for variables:

questionnaire.questions.map {
  case q @ Question(text, a @ Answer(d:Double)) => //
  case q @ Question(text, a @ Answer(s:String)) => //
  case q @ Question(text, a @ Answer(ld:LocalDate)) => //
}
like image 36
Nyavro Avatar answered Dec 21 '22 00:12

Nyavro