Как обойти использование параметра ковариантного типа в контравариантной позиции - PullRequest
0 голосов
/ 04 декабря 2018

Как часть системы управления конвейером / рабочим процессом / выполнением, у меня есть внутренний 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

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

1 Ответ

0 голосов
/ 05 декабря 2018

Почему "страшно", что "страшно" об этом ...

Стандартный обходной путь:

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)
    def |[T >: Out](next: Option[Pipe[T, T]]): Pipe[In, T] = next match {
      case Some(n) => this | n
      case None    => this
    }
  }

  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(Column()) | Option.empty[Pipe[Text, Text]] | Column() 
...