Scala-каррирование и аргументы по умолчанию


У меня есть функция с двумя списками параметров, которые я пытаюсь частично применить и использовать с каррингом. Второй список параметров содержит аргументы, которые имеют значения по умолчанию (но не являются неявными). Что-то вроде этого:

 def test(a: Int)(b: Int = 2, c: Int = 3) { println(a + ", " + b + ", " + c); }

Теперь все в порядке:

 test(1)(2, 3);
 test(1)(2);
 test(1)(c=3);
 test(1)();

Теперь, если я определю:

 def partial = test(1) _;

Тогда можно сделать следующее:

 partial(2, 3);

Может кто-нибудь объяснить, почему я не могу опустить некоторые/все аргументы в "partial" следующим образом:

 partial(2);
 partial(c=3);
 partial();

Не следует писать "частичные" ведут себя по существу так же, как и"тест(1)"? Может кто-нибудь помочь мне найти способ достичь этого?

Пожалуйста, помогите, я в отчаянии!

EDIT - поскольку я не могу ответить на свой собственный вопрос в течение 24 часов, я опубликую свой собственный ответ здесь:

Это лучшее, что я мог сделать сам до сих пор:

class Test2(val a: Int) {
   def apply(b: Int = 2, c: Int = 3) { println(a + ", " + b + ", " + c); }
}

def test2(a: Int) = new Test2(a);
def partial2 = test2(1); // Note no underscore

test2(1)(2, 3);
test2(1)(2);
test2(1)(c=3);
test2(1)();

partial2(2, 3)
partial2(2);
partial2(c=3);
partial2();

Вот как это работает...

3 10

3 ответа:

Механизм вывода типа дает partial тип того, что происходит дальше; т. е. расширение eta test(1) _. Вы можете видеть, например, в REPL, что partial имеет тип (Int, Int) => Unit, тогда как test имеет тип (a: Int)(b: Int,c: Int)Unit. Результатом расширения eta является объект Function, который не несет с собой никаких имен аргументов (так как можно определить Function с анонимными параметрами).

Чтобы исправить это, вы должны определить partial следующим образом:

def partial(b: Int = 2, c: Int = 3) = test(1)(b,c)

Может быть, вы захотите разложить значения по умолчанию, где и test, и partial могут достичь их, чтобы убедиться, что они остаются равными. Но я не знаю никакого трюка, чтобы избежать повторения имен параметров без введения дополнительных накладных расходов, таких как создание новых объектов и т. д.

Это лучшее, что я мог сделать сам до сих пор:

class Test2(val a: Int) {
   def apply(b: Int = 2, c: Int = 3) { println(a + ", " + b + ", " + c); }
}

def test2(a: Int) = new Test2(a);
def partial2 = test2(1); // Note no underscore

test2(1)(2, 3);
test2(1)(2);
test2(1)(c=3);
test2(1)();

partial2(2, 3)
partial2(2);
partial2(c=3);
partial2();

Вот как это работает...

Следуя вашему комментарию, вот более компактный способ его написания:

def test(a: Int) = new {
  def apply(b: Int = 2, c: Int = 3) {
    println(a + ", " + b + ", " + c)
  }
}
Это немного более компактно, чем ваше предложение, но менее эффективно, так как любой вызов внутреннего apply будет происходить через рефлексию, как и в случае со структурными типами. На самом деле возвращаемый тип test является структурным типом:
 java.lang.Object{def apply(b: Int,c: Int): Unit; def apply$default$1: 
 Int @scala.annotation.unchecked.uncheckedVariance; def apply$default$2: Int 
 @scala.annotation.unchecked.uncheckedVariance}