Класс случая Scala: создание экземпляров для случаев конструктора типов?

Я хочу использовать общую функцию, которая возвращает «значение по умолчанию» для данного типа. Я реализовал это с помощью следующей конструкции класса case:

case class DefaultOp[T](op: () => T)
implicit val defaultString = DefaultOp[String](() => "")
implicit val defaultInt = DefaultOp[Int](() => 0)
implicit val defaultFloat = DefaultOp[Float](() => 0.0f)
implicit val defaultDouble = DefaultOp[Double](() => 0.0d)
// ...

def default[T]()(implicit ev: DefaultOp[T]): T = ev.op()

Это прекрасно работает, но я хотел бы расширить его, чтобы он также работал с типами, созданными из конструктора типов. Например, я хотел бы, чтобы значение по умолчанию для любого типа параметра было «Нет», а для любого типа списка — «Ноль». Как я могу это реализовать? Моя первая попытка:

implicit val defaultOption = DefaultOp[Option[_]](() => None)

компилируется, но не компилируется, когда я использую его в приложении:

error: could not find implicit value for parameter ev: DefaultOp[Option[Float]]
var test: Option[Float] = default[Option[Float]]
                                  ^
one error found

Любое предложение, как это можно заставить работать?

Спасибо!


person Da Li    schedule 06.02.2015    source источник


Ответы (1)


Проблема в том, что Option[Float] не является Option[_], потому что мы не знаем, каким должно быть _.

Вы можете решить эту проблему, сделав defaultOption определением и используя параметр типа:

implicit def defaultOption[A] = DefaultOp[Option[A]](() => None)

scala> default[Option[Float]]
res3: Option[Float] = None
person Michael Zajac    schedule 06.02.2015
comment
Спасибо! Вот и все. Я не знал о том, что я могу использовать определение функции вместо val... что приводит к следующему вопросу: в чем принципиальная разница между неявным def defaultDouble = DefaultOp[Double](() => 0.0d) и неявное значение val defaultDouble = DefaultOp[Double](() => 0.0d)? Какой из них предпочтительнее и почему? - person Da Li; 06.02.2015
comment
Разница лишь в том, что один — def, а другой — val. Для defaultDouble можно использовать val, потому что он никогда не изменится. Однако для универсального типа. требуется определение, потому что вам нужно иметь возможность генерировать неявные значения для (возможно) любого типа. - person Michael Zajac; 06.02.2015
comment
Также обратите внимание, что def будет создавать новый экземпляр DefaultOp для каждого вызова. Если это находится на вашем критическом пути, возможно, вам это не нужно. Затем вы можете обмануть с помощью этого кода: private val reusableDefaultOption = DefaultOp[Option[Nothing]](() => None); implicit def defaultOption[A] = reusableDefaultOption.asInstanceOf[DefaultOp[Option[A]]]. - person sjrd; 06.02.2015