Как часть системы управления конвейером / рабочим процессом / выполнением, у меня есть внутренний DSL для описания исполняемых задач, и этот DSL имеет конструкции, позволяющие соединять задачи через каналы (DSL выглядит как каналы unix, а реализация ниже)на самом деле через Unix-каналы).
Ниже приведен отдельный, чрезвычайно упрощенный пример, который компилируется:
class Void
class Data
class Text extends Data
class Csv extends Text
class Tsv extends Text
object Piping {
trait Pipe[-In,+Out] {
def |[Out2](next: Pipe[Out,Out2]): Pipe[In,Out2] = Chain(this, next)
}
case class Chain[-A,+B](a: Pipe[A,_], b: Pipe[_,B]) extends Pipe[A,B]
case class Cat() extends Pipe[Void,Text]
case class MakeCsv() extends Pipe[Text,Csv]
case class MakeTsv() extends Pipe[Text,Tsv]
case class CsvToTsv() extends Pipe[Csv,Tsv]
case class Column() extends Pipe[Text,Text]
}
import Piping._
Cat() | MakeCsv() | Column()
Он использует параметры типа, чтобы гарантировать, что вы не можете передавать данные между вещами, которые ожидают разные типы данных, и все этоработает.
Теперь я хочу добавить метод, который позволяет выполнять конвейерную обработку для необязательной задачи, которая может быть либо Some[Pipe]
, либо None
, и вот здесь начинается моя проблема:
Я хочу расширить эту черту следующим образом:
trait Pipe[-In,+Out] {
def |[Out2](next: Pipe[Out,Out2]): Pipe[In,Out2] = Chain(this, next)
def |(next: Option[Pipe[Out,Out]]): Pipe[In,Out] = next match {
case Some(n) => this | n
case None => this
}
}
Или по-английски, я хочу взять Option[Pipe]
, чей тип ввода - любой супертип Out
, а тип вывода -любой подтип Out
.Однако это вызывает у меня страх:
error: covariant type Out occurs in contravariant position in type Option[Piping.Pipe[Out,Out]] of value next
Я понимаю, почему я получаю ошибку, но я не могу понять, есть ли способ выразить отношения типа, которые я хочу, которые не вызовутэта ошибка!