Почему Scala не может вывести аргумент типа, когда он очевиден?
В следующем примере я пытался создать неявное преобразование между MySource
а также TypedPipe[T]
, я владею MySource
На самом деле у меня много таких источников, поэтому я хотел использовать Porable[T]
черта, чтобы отметить, какой тип аргумента T
Я хочу за выход TypedPipe[T]
, так что неявное преобразование может автоматически делать .toTypedPipe[T]
часть (так что мне не нужно писать .toTypedPipe[T]
для каждого источника, когда я их использую).
import scala.language.implicitConversions
// The following two are pre-defined and I cannot change them
class TypedPipe[T](val path: String) {
def mapWithValue = {
println("values from " + path + " of type " + this.getClass)
}
}
class Source(val path: String) {
def toTypedPipe[T] = { new TypedPipe[T](path) }
}
// The following are defined by me, so yes I can change them.
trait Portable[T]
class MySource(p: String) extends Source(p) with Portable[Tuple2[Int, Long]]
object conversions {
implicit def portableSourceToTypedPipe[S <: Source with Portable[T], T](source: S): TypedPipe[T] = {
source
.toTypedPipe[T]
}
}
import conversions._
portableSourceToTypedPipe(new MySource("real_path")).mapWithValue
Но проблема в том, что Скала, похоже, не сможет сделать вывод T
для последнего утверждения:
scala> import scala.language.implicitConversions
import scala.language.implicitConversions
scala> class TypedPipe[T](val path: String) {
| def mapWithValue = {
| println("values from " + path + " of type " + this.getClass)
| }
| }
defined class TypedPipe
scala> class Source(val path: String) {
| def toTypedPipe[T] = { new TypedPipe[T](path) }
| }
defined class Source
scala>
scala> trait Portable[T]
defined trait Portable
scala>
scala> class MySource(p: String) extends Source(p) with Portable[Tuple2[Int, Long]]
defined class MySource
scala> object conversions {
| implicit def portableSourceToTypedPipe[S <: Source with Portable[T], T](source: S): TypedPipe[T] = {
| source
| .toTypedPipe[T]
| }
| }
defined module conversions
scala> import conversions._
import conversions._
scala> portableSourceToTypedPipe(new MySource("real_path")).mapWithValue
<console>:17: error: inferred type arguments [MySource,Nothing] do not conform to method portableSourceToTypedPipe's type parameter bounds [S <: Source with Portable[T],T]
portableSourceToTypedPipe(new MySource("real_path")).mapWithValue
^
<console>:17: error: type mismatch;
found : MySource
required: S
portableSourceToTypedPipe(new MySource("real_path")).mapWithValue
^
scala>
Из примера видно, что MySource
инвентарь Portable[Tuple2[Int, Long]]
, так T
должно быть Tuple2[Int, Long]
, Почему это не выводится таким образом (что сделало бы пример работоспособным)?
РЕДАКТИРОВАТЬ:
После этого вопроса и его ответа, упомянутого n.m., я изменил свой код, чтобы использовать неявный параметр доказательства, чтобы выразить связь между двумя типами аргументов. Явный вызов преобразования работает сейчас, но не неявный вызов преобразования. Так что все еще нужна помощь.
scala> object conversions {
| implicit def portableSourceToTypedPipe[S, T](source: S)(implicit ev: S <:< Source with Portable[T]): TypedPipe[T] = {
| source
| .toTypedPipe[T]
| }
| }
defined module conversions
scala> import conversions._
import conversions._
scala> portableSourceToTypedPipe(new MySource("real_path")).mapWithValue
values from real_path of type class $line4.$read$$iw$$iw$TypedPipe
scala> (new MySource("real_path")).mapWithValue
<console>:17: error: Cannot prove that MySource <:< Source with Portable[T].
(new MySource("real_path")).mapWithValue
^
<console>:17: error: value mapWithValue is not a member of MySource
(new MySource("real_path")).mapWithValue
EDIT2
Причина для меня, чтобы выбрать черту Portable[T]
является то, что он может потенциально работать с несколькими базами Source
типы. Кто-то, знакомый со Scalding, может знать, что у нас есть много типов источников, например DailySuffixSource, HourlySuffixSource, не говоря уже о том, что можно подключить другие черты типа SuccessFileSource
а также DelimitedScheme
, Необходимость реализовать что-то для каждой комбинации базовый источник / черты потребует довольно много работы. Таким образом, моя черта выбора. Конечно, это не обязательно - подойдет любой ответ, который может с несколькими базовыми комбинациями источник / черты с O(1) количеством реализации.
2 ответа
Учитывая, что вы не используете параметр типа S нигде в возвращаемом типе, почему бы просто не иметь portableSourceToTypedPipe
взять Source with Portable[T]
, Другими словами:
implicit def portableSourceToTypedPipe[T](source: Source with Portable[T]): TypedPipe[T]
Это тривиально устраняет проблему компиляции. В общем, чем более явным вы являетесь, тем выше вероятность того, что компилятор сможет разрешить ограничения, представленные параметрами типа. Это начинается с полного удаления ненужных параметров типа.
Ваш Source
определение говорит, что вы можете позвонить toTypedPipe[T]
для любого T
, Если вы действительно хотите MySource
преобразовать в Tuple2[Int, Long]
только, это должно быть
class TypedSource[T](path: String) extends Source(path) {
def toSpecificTypedPipe = toTypedPipe[T]
}
class MySource(p: String) extends TypedSource[(Int, Long)](p)
implicit def portableSourceToTypedPipe[T](source: TypedSource[T]): TypedPipe[T] =
source.toSpecificTypedPipe
(Вы также можете использовать композицию вместо наследования для TypedSource
.)
Если вы хотите, чтобы способность конвертировалась в любой тип с одним "предпочтительным", просто избавьтесь от S
Вам это не нужно:
implicit def portableSourceToTypedPipe[T](source: Source with Portable[T]): TypedPipe[T]