абстрагироваться от функций scala без потери типов

Я делаю функцию, которая принимает лямбду и использует .tupled, если это возможно (арность 2+). Чтобы компилятор разрешил это использовать, ему нужно знать, действительно ли лямбда является функцией Function2 (~ Function22). Однако сопоставление с образцом для Function2[Any,Any,Any] означает, что я остаюсь с (Any, Any) => Any, а не с его исходными типами. Даже незнание арности тоже не помогает. Я пробовал сопоставлять, например, case f[A <: Any, B <: Any, C <: Any]: scala.Function2[A, B, C] => f.tupled, пытаясь сохранить типы, но на самом деле это не позволяет использовать параметры типа в случае.

Код:

val add = (a: Int, b: Int) => a + b
add: (Int, Int) => Int = <function2>

val tpl = (fn: Any) => {
  fn match {
    case f: Function0[Any] => f
    case f: Function1[Any,Any] => f
    case f: Function2[Any,Any,Any] => f.tupled
//  case f: Function3[Any,Any,Any,Any] => f.tupled
//  ...
//  case _ => { throw new Exception("huh") }
  }
}

// actual result:
tpl(add)
res0: Any = <function1>

// desired result is like this one:
scala> add.tupled
res3: ((Int, Int)) => Int = <function1>

Бонусные баллы, если мне не понадобятся случаи сопоставления с образцом для каждого возможного уровня арности...


person Kiara Grouwstra    schedule 07.09.2015    source источник
comment
Это невозможно. Каким может быть тип возвращаемого значения? Function1[(A, B)]? Function1[(A, B, C)]? Function[(A, B, C, D)]?   -  person dcastro    schedule 07.09.2015
comment
Я считаю, что вам нужны зависимые типы, чтобы не обрабатывать каждую арность отдельно. Возможно, вам придется переключиться на Agda или Idris или что-то подобное.   -  person Lii    schedule 07.09.2015
comment
@dcastro: хорошая мысль. Я полагаю, что я был более знаком с языками с динамической типизацией, и у меня были проблемы с разрывом между тем, как типы могут оказаться на практике, и гарантиями «наибольшего общего знаменателя», которые компилятор может сделать о них...   -  person Kiara Grouwstra    schedule 07.09.2015
comment
@Lii: спасибо за рекомендации; Мне придется запомнить их. На данный момент я надеюсь не усложнять свой проект еще больше, хотя...   -  person Kiara Grouwstra    schedule 07.09.2015


Ответы (1)


Ответ уродлив, как вы могли ожидать. Использование сопоставления с образцом для функции в качестве val не сработает. Когда вы начинаете с Any, вы уже потеряли массу информации о типах. И стандартная библиотека тоже не очень помогает, так как нет абстракции над арностью функций. Это означает, что мы даже не можем использовать отражение, чтобы попытаться получить параметры типа, потому что мы даже не знаем, сколько их. Вы можете выяснить, какие FunctionN у вас есть, но не содержащиеся в них типы, так как на данный момент они уже потеряны.

Другая возможность — сделать tpl методом и перегружать его для каждого FunctionN.

def tpl[A](f: Function0[A]): Function0[A] = f
def tpl[A, R](f: Function1[A, R]): Function1[A, R] = f
def tpl[A1, A2, R](f: Function2[A1, A2, R]): Function1[(A1, A2), R] = f.tupled
def tpl[A1, A2, A3, R](f: Function3[A1, A2, A3, R]): Function1[(A1, A2, A3), R] = f.tupled
// ... and so on

scala> val add = (a: Int, b: Int) => a + b
add: (Int, Int) => Int = <function2>

scala> tpl(add)
res0: ((Int, Int)) => Int = <function1>

Это не очень красиво, но, по крайней мере, безопасно для типов. Я не думаю, что было бы слишком сложно создать макрос для генерации перегрузок 1-22.

person Michael Zajac    schedule 07.09.2015
comment
Спасибо. Как вы сказали, это не становится супер красиво, но да, любые кусочки помогают. Я также сейчас изучаю макросы — даже если обычная функция не может легко сделать это из-за потери типа, добавление операций с помощью макроса (например, .tupled) является интересной альтернативой. Для чего-то такого короткого макрос, казалось бы, имеет небольшую добавленную стоимость, но, очевидно, это была упрощенная версия моей функции — для чего-то большего, что могло бы действительно элегантно решать проблемы. Спасибо за ваш вклад; считайте этот вопрос решенным. - person Kiara Grouwstra; 07.09.2015