Моя функция получает другую функцию (которая сопоставляет тип ввода с типом вывода) в качестве параметра:

type Handled[S,R] = S => R

def myFunc[S,R](value: S, handled: Handled[S,R] = defaultHandled): R = {
  handled(value)
}

Мне нужно написать функцию defaultHandled, которая получит тип ввода и вернет его как есть.

Поэтому по умолчанию я хочу сопоставить тип ввода с типом вывода, где тип ввода совпадает с типом вывода. Эта функция должна просто передавать ввод для вывода для любого типа ввода. Как это сделать?

2
Antek 10 Сен 2017 в 08:58

3 ответа

Лучший ответ

Хотя технически это возможно:

type Handled[S, R] = S => R

def defaultHandled[S, R](x: S): R = x.asInstanceOf[R]

def myFunc[S, R](value: S, handled: Handled[S, R] = defaultHandled[S, R] _): R = {
  handled(value)
}

myFunc[Int, Int](1)

Это небезопасно и вообще не очень хорошая идея. Например, если вы попытаетесь вызвать myFunc с параметрами другого типа, все еще полагаясь на значение по умолчанию handled, вы получите исключение времени выполнения:

myFunc[Int, String](1)

java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String

Способ Scala для решения этой проблемы должен быть обработан как неявный параметр. В этом случае вы можете указать реализацию по умолчанию, которая будет использоваться компилятором.

type Handled[S, R] = S => R

implicit def defaultHandled[S]: Handled[S, S] = identity

def myFunc[S, R](value: S)(implicit handled: Handled[S, R]): R = {
  handled(value)
}

myFunc(1) // compiles and works

myFunc[Int, String](1) // compilation error: Error:(11, 21) No implicit view 
                       // available from Int => String.
                       //myFunc[Int, String](1)
                       //            ^
5
Aivean 10 Сен 2017 в 07:11

Если S и R - два произвольных типа (которые не зависят друг от друга), то я думаю, что это невозможно (без приведения во время выполнения). Вы должны предоставить defaultHandled для каждого S, R. И s => s имеет тип S => S, а не S => R для произвольного R.

Но если тип R зависит от типа S, вы можете сделать следующее:

  trait Handled[S] {
    type R
    def apply(s: S): R
  } 
  object Handled {
    type Aux[S, R0] = Handled[S] {type R = R0}
    def apply[S, R0](f: S => R0): Aux[S, R0] = new Handled[S] {
      override type R = R0
      override def apply(s: S): R = f(s)
    }
  }


  def myFunc[S](value: S, handled: Handled[S] = Handled[S, S](s => s)): handled.R = {
    handled(value)
  }

Здесь Handled[S] (в отличие от Handled.Aux[S, R]) является экзистенциальный тип.

0
Dmytro Mitin 10 Сен 2017 в 07:44

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

type Handled[S,R] = S => R

def myFunc[S,R](value: S, handled: Handled[S,R]): R = {
  handled(value)
}

def myFunc[S](value: S): S = value
2
Alexey Romanov 10 Сен 2017 в 08:28