Синтаксис такого типа - это вызовы методов в нотации операторов, но они переносятся более чем на три токена. Как вы уже упоминали:
xs map f
означает:
xs.map(f)
Но вы могли бы пойти дальше и сказать:
xs map f map g
, что означает:
xs.map(f).map(g)
Например, в сопоставителях ScalaTest вы можете сказать:
result should not be null
Это компилируется с помощью компилятора:
result.should(not).be(null)
Это:
it should "throw an exception" in { ... }
переводится в:
it.should("throw an exception").in { ... }
Фигурные скобки в конце - это на самом деле способ передачи кода между фигурными скобками (тестовый код) в метод in, заключенный в функцию без аргументов. Так что все это одна и та же идея. Обозначение оператора используется дважды подряд.
Последний вопрос, о котором вы спрашивали, немного другой:
evaluating { ... } should produce [IllegalArgumentException]
Это преобразуется в:
evaluating { ... }
сначала оценивается, потому что фигурные скобки дают ему приоритет. Так что это вызов метода, вы вызываете метод с именем «вычисления», передавая код между фигурными скобками как функцию без аргументов. Это возвращает объект, для которого вызывается should
. Таким образом, should
- это метод для объекта, возвращаемого путем вызова evaluating
. То, что should
на самом деле берет, является результатом вызова produce
. Здесь produce
на самом деле является методом, который имеет параметр типа, например [IllegalArgumentException]
. Это должно быть сделано таким образом, чтобы компилятор Scala мог «переиграть бедняков» этого параметра типа. Он передает неявный параметр "Manifest" в produce
, который может предоставить экземпляр java.lang.Class
для IllegalArgumentException
. Следовательно, когда вызывается метод should, он имеет функцию, содержащую код, переданный в evaluating
, и способ найти java.lang.Class
типа исключения, заключенного в квадратные скобки. Таким образом, он выполняет блок кода, заключенный в try
, ловит исключение, сравнивает его с ожидаемым. Если исключение не выбрано, или неправильное, метод should
выдает TestFailedException
. В противном случае метод should
просто возвращает без вывода сообщений.
Итак, ответ в том, что строка обнуляется:
(evaluating { ... }).should(produce[IllegalArgumentException] (compilerSuppliedManifest))
И мораль этой истории в том, что подобный код высокого уровня облегчает понимание намерений программиста, но зачастую труднее понять, как на самом деле работает код. В большинстве случаев на практике все, что вас волнует, - это намерение, но время от времени вам нужно знать, как что-то работает. В таких случаях в Scala вы можете передать -Xprint:typer
в качестве аргумента командной строки компилятору Scala, и он распечатает версию вашего файла после того, как произойдет десагеринг. Так что вы можете видеть, что к чему, когда вам нужно.