Можно ли записать зависимый тип функции в Scala?

Учитывая

trait Foo {
  type Bar
}

существует ли законный способ написать что-то вроде f: (x: Foo) => x.Bar, так что возвращаемый тип функции зависит от аргумента? Примером использования может быть

def compareOutput(x1: Foo, x2: Foo)(f: (x: Foo) => x.Bar /* illegal */)(comparer: (x1.Bar, x2.Bar) => Boolean) = {
  val y1 = f(x1)
  val y2 = f(x2)
  comparer(y1, y2)
}

Ответы

Ответ 1

Scala не имеет полиморфных функций, как, например, Haskell. Тип функции всегда конкретный. Только методы могут быть полиморфными. К сожалению, это означает, что ваш гипотетический тип (x: Foo) => x.Bar не выражен в Scala.

Это становится очевидным при преобразовании общих методов в функции: вся информация о типе теряется:

scala> def foo[Bar](x: Bar): Bar = ???
foo: [Bar](x: Bar)Bar

scala> foo _
res1: Nothing => Nothing = <function1>

Возможно, вы также заметили, что scala не может преобразовать методы с зависимыми типами в функции. Это потому, что невозможно удовлетворить ваш гипотетический тип:

scala> def foo(x: Foo): x.Bar = ???
foo: (x: Foo)x.Bar

scala> foo _
<console>:10 error: method with dependent type (x: Foo)x.Bar cannot be converted 
                    to function value.

Также см. этот и этот.


Однако есть несколько решений вашей проблемы. Можно было бы инкапсулировать ваш зависимый типизированный метод в вспомогательный признак:

trait FooFunc {
  def apply(x: Foo): x.Bar
}

def compareOutput(x1: Foo, x2: Foo)(f: FooFunc)(comparer: ...) = {
  val y1 = f(x1)
  val y2 = f(x2)
  comparer(y1,y2)
}

Затем вы можете создавать экземпляры FooFunc, которые вы можете передать в свой метод compareOutput.